firewatir 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. data/MozillaBaseElement.rb +1778 -0
  2. data/container.rb +889 -0
  3. data/firewatir/exceptions.rb +44 -0
  4. data/firewatir/testUnitAddons.rb +8 -0
  5. data/firewatir.rb +1130 -0
  6. data/htmlelements.rb +2277 -0
  7. data/unittests/attach_to_new_window_test.rb +34 -0
  8. data/unittests/bug_fixes_test.rb +188 -0
  9. data/unittests/buttons_test.rb +215 -0
  10. data/unittests/buttons_xpath_test.rb +87 -0
  11. data/unittests/checkbox_test.rb +154 -0
  12. data/unittests/checkbox_xpath_test.rb +107 -0
  13. data/unittests/div_test.rb +274 -0
  14. data/unittests/filefield_test.rb +45 -0
  15. data/unittests/filefield_xpath_test.rb +35 -0
  16. data/unittests/form_test.rb +307 -0
  17. data/unittests/frame_test.rb +151 -0
  18. data/unittests/hidden_test.rb +85 -0
  19. data/unittests/hidden_xpath_test.rb +72 -0
  20. data/unittests/html/JavascriptClick.html +42 -0
  21. data/unittests/html/blankpage.html +12 -0
  22. data/unittests/html/buttons1.html +61 -0
  23. data/unittests/html/checkboxes1.html +71 -0
  24. data/unittests/html/complex_table.html +36 -0
  25. data/unittests/html/cssTest.html +42 -0
  26. data/unittests/html/div.html +72 -0
  27. data/unittests/html/div_xml.html +21 -0
  28. data/unittests/html/fileupload.html +45 -0
  29. data/unittests/html/formTest1.html +39 -0
  30. data/unittests/html/forms2.html +45 -0
  31. data/unittests/html/forms3.html +132 -0
  32. data/unittests/html/forms4.html +27 -0
  33. data/unittests/html/frame_buttons.html +4 -0
  34. data/unittests/html/frame_links.html +4 -0
  35. data/unittests/html/frame_multi.html +5 -0
  36. data/unittests/html/iframeTest.html +15 -0
  37. data/unittests/html/iframeTest1.html +14 -0
  38. data/unittests/html/iframeTest2.html +6 -0
  39. data/unittests/html/images/1.gif +0 -0
  40. data/unittests/html/images/2.GIF +0 -0
  41. data/unittests/html/images/3.GIF +0 -0
  42. data/unittests/html/images/button.jpg +0 -0
  43. data/unittests/html/images/circle.jpg +0 -0
  44. data/unittests/html/images/minus.GIF +0 -0
  45. data/unittests/html/images/originaltriangle.jpg +0 -0
  46. data/unittests/html/images/plus.gif +0 -0
  47. data/unittests/html/images/square.jpg +0 -0
  48. data/unittests/html/images/triangle.jpg +0 -0
  49. data/unittests/html/images1.html +67 -0
  50. data/unittests/html/javascriptevents.html +35 -0
  51. data/unittests/html/link_pass.html +11 -0
  52. data/unittests/html/links1.html +42 -0
  53. data/unittests/html/links2.html +11 -0
  54. data/unittests/html/modal_dialog.html +8 -0
  55. data/unittests/html/modal_dialog_launcher.html +12 -0
  56. data/unittests/html/nestedFrames.html +6 -0
  57. data/unittests/html/new_browser.html +17 -0
  58. data/unittests/html/pass.html +10 -0
  59. data/unittests/html/popups1.html +60 -0
  60. data/unittests/html/pre.html +28 -0
  61. data/unittests/html/radioButtons1.html +71 -0
  62. data/unittests/html/redirect.html +10 -0
  63. data/unittests/html/redirect1.html +9 -0
  64. data/unittests/html/redirect2.html +9 -0
  65. data/unittests/html/redirect3.html +9 -0
  66. data/unittests/html/select_tealeaf.html +54 -0
  67. data/unittests/html/selectboxes1.html +55 -0
  68. data/unittests/html/simple_table.html +26 -0
  69. data/unittests/html/simple_table_buttons.html +104 -0
  70. data/unittests/html/simple_table_columns.html +74 -0
  71. data/unittests/html/table1.html +165 -0
  72. data/unittests/html/tableCell_using_xpath.html +19 -0
  73. data/unittests/html/textarea.html +30 -0
  74. data/unittests/html/textfields1.html +62 -0
  75. data/unittests/html/textsearch.html +44 -0
  76. data/unittests/images_test.rb +204 -0
  77. data/unittests/images_xpath_test.rb +118 -0
  78. data/unittests/iostring.rb +30 -0
  79. data/unittests/iostring_test.rb +48 -0
  80. data/unittests/javascript_test.rb +71 -0
  81. data/unittests/links_test.rb +230 -0
  82. data/unittests/links_xpath_test.rb +79 -0
  83. data/unittests/mozilla_all_tests.rb +18 -0
  84. data/unittests/pre_test.rb +74 -0
  85. data/unittests/radios_test.rb +166 -0
  86. data/unittests/radios_xpath_test.rb +101 -0
  87. data/unittests/redirect_test.rb +40 -0
  88. data/unittests/selectbox_test.rb +141 -0
  89. data/unittests/selectbox_xpath_test.rb +127 -0
  90. data/unittests/setup.rb +35 -0
  91. data/unittests/table_test.rb +372 -0
  92. data/unittests/table_xpath_test.rb +184 -0
  93. data/unittests/textfields_test.rb +230 -0
  94. data/unittests/textfields_xpath_test.rb +112 -0
  95. metadata +144 -0
data/firewatir.rb ADDED
@@ -0,0 +1,1130 @@
1
+ =begin
2
+ license
3
+ ---------------------------------------------------------------------------
4
+ Copyright (c) 2006-2007, Angrez Singh
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice,
10
+ this list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright
13
+ notice, this list of conditions and the following disclaimer in the
14
+ documentation and/or other materials provided with the distribution.
15
+
16
+ 3. Neither the names Angrez Singh nor the names of contributors to
17
+ this software may be used to endorse or promote products derived from this
18
+ software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
21
+ IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
24
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27
+ OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
30
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+ --------------------------------------------------------------------------
32
+ (based on BSD Open Source License)
33
+ =end
34
+
35
+ =begin rdoc
36
+ This is FireWatir, Web Application Testing In Ruby using Firefox browser
37
+ The home page for this project is http://code.google.com/p/firewatir
38
+
39
+ Typical usage:
40
+ # include the controller
41
+ require "firewatir"
42
+
43
+ # go to the page you want to test
44
+ ff = FireWatir::Firefox.start("http://myserver/mypage")
45
+
46
+ # enter "Angrez" into an input field named "username"
47
+ ff.text_field(:name, "username").set("Angrez")
48
+
49
+ # enter "Ruby Co" into input field with id "company_ID"
50
+ ff.text_field(:id, "company_ID").set("Ruby Co")
51
+
52
+ # click on a link that has "green" somewhere in the text that is displayed
53
+ # to the user, using a regular expression
54
+ ff.link(:text, /green/)
55
+
56
+ # click button that has a caption of "Cancel"
57
+ ff.button(:value, "Cancel").click
58
+
59
+ FireWatir allows your script to read and interact with HTML objects--HTML tags
60
+ and their attributes and contents. Types of objects that FireWatir can identify
61
+ include:
62
+
63
+ Type Description
64
+ =========== ===============================================================
65
+ button <input> tags, with the type="button" attribute
66
+ check_box <input> tags, with the type="checkbox" attribute
67
+ div <div> tags
68
+ form
69
+ frame
70
+ hidden hidden <input> tags
71
+ image <img> tags
72
+ label
73
+ link <a> (anchor) tags
74
+ p <p> (paragraph) tags
75
+ radio radio buttons; <input> tags, with the type="radio" attribute
76
+ select_list <select> tags, known informally as drop-down boxes
77
+ span <span> tags
78
+ table <table> tags
79
+ text_field <input> tags with the type="text" attribute (a single-line
80
+ text field), the type="text_area" attribute (a multi-line
81
+ text field), and the type="password" attribute (a
82
+ single-line field in which the input is replaced with asterisks)
83
+
84
+ In general, there are several ways to identify a specific object. FireWatir's
85
+ syntax is in the form (how, what), where "how" is a means of identifying
86
+ the object, and "what" is the specific string or regular expression
87
+ that FireWatir will seek, as shown in the examples above. Available "how"
88
+ options depend upon the type of object, but here are a few examples:
89
+
90
+ How Description
91
+ ============ ===============================================================
92
+ :id Used to find an object that has an "id=" attribute. Since each
93
+ id should be unique, according to the XHTML specification,
94
+ this is recommended as the most reliable method to find an
95
+ object.
96
+ :name Used to find an object that has a "name=" attribute. This is
97
+ useful for older versions of HTML, but "name" is deprecated
98
+ in XHTML.
99
+ :value Used to find a text field with a given default value, or a
100
+ button with a given caption
101
+ :index Used to find the nth object of the specified type on a page.
102
+ For example, button(:index, 2) finds the second button.
103
+ Current versions of FireWatir use 1-based indexing, but future
104
+ versions will use 0-based indexing.
105
+ :xpath The xpath expression for identifying the element.
106
+
107
+ Note that the XHTML specification requires that tags and their attributes be
108
+ in lower case. FireWatir doesn't enforce this; FireWatir will find tags and
109
+ attributes whether they're in upper, lower, or mixed case. This is either
110
+ a bug or a feature.
111
+
112
+ FireWatir uses JSSh for interacting with the browser. For further information on
113
+ Firefox and DOM go to the following Web page:
114
+
115
+ http://www.xulplanet.com/references/objref/
116
+
117
+ =end
118
+
119
+ require 'firewatir/exceptions'
120
+ require 'container'
121
+ require 'MozillaBaseElement.rb'
122
+ require 'htmlelements.rb'
123
+ require 'socket'
124
+
125
+ class String
126
+ def matches (x)
127
+ return self == x
128
+ end
129
+ end
130
+
131
+ class Regexp
132
+ def matches (x)
133
+ return self.match(x)
134
+ end
135
+ end
136
+
137
+ class Integer
138
+ def matches (x)
139
+ return self == x
140
+ end
141
+ end
142
+
143
+ module FireWatir
144
+ include FireWatir::Exception
145
+
146
+ class Firefox
147
+
148
+ include Container
149
+
150
+ # XPath Result type. Return only first node that matches the xpath expression.
151
+ # More details: "http://developer.mozilla.org/en/docs/DOM:document.evaluate"
152
+ FIRST_ORDERED_NODE_TYPE = 9
153
+
154
+ VERSION = '1.1.1'
155
+
156
+ # variable to check if firefox browser has been started or not. Currently this is
157
+ # used only while starting firefox on windows. For other platforms you need to start
158
+ # firefox manually.
159
+ #@@firefox_started = false
160
+
161
+ # Stack to hold windows.
162
+ @@window_stack = Array.new
163
+
164
+ # This allows us to identify the window uniquely and close them accordingly.
165
+ @window_title = nil
166
+ @window_url = nil
167
+
168
+
169
+ #
170
+ # Description:
171
+ # Starts the firefox browser. Currently this only works for Windows Platform.
172
+ # For others, you need to start Firefox manually using -jssh option.
173
+ # On windows this starts the first version listed in the registry.
174
+ #
175
+ # Input:
176
+ # options - Hash of any of the following options:
177
+ # :waitTime - Time to wait for Firefox to start. By default it waits for 2 seconds.
178
+ # This is done because if Firefox is not started and we try to connect
179
+ # to jssh on port 9997 an exception is thrown.
180
+ # :profile - The Firefox profile to use. If none is specified, Firefox will use
181
+ # the last used profile. Note that the :profile option only works if
182
+ # no other instances of Firefox are already running.
183
+
184
+ # TODO: Start the firefox version given by user. For example
185
+ # ff = FireWatir::Firefox.new("1.5.0.4")
186
+ #
187
+
188
+ def initialize(options = {})
189
+ if(options.kind_of?(Integer))
190
+ options = {:waitTime => options}
191
+ end
192
+
193
+ if(options[:profile])
194
+ profile_opt = "-P #{options[:profile]}"
195
+ else
196
+ profile_opt = ""
197
+ end
198
+
199
+ puts "PROFILE: #{profile_opt}"
200
+
201
+ waitTime = options[:waitTime] || 2
202
+
203
+ if(RUBY_PLATFORM =~ /.*mswin.*/)
204
+ #puts "plaftorm is windows"
205
+ # Get the path to Firefox.exe using Registry.
206
+ require 'win32/registry.rb'
207
+ path_to_exe = ""
208
+ Win32::Registry::HKEY_LOCAL_MACHINE.open('SOFTWARE\Mozilla\Mozilla Firefox') do |reg|
209
+ keys = reg.keys
210
+ reg1 = Win32::Registry::HKEY_LOCAL_MACHINE.open("SOFTWARE\\Mozilla\\Mozilla Firefox\\#{keys[0]}\\Main")
211
+ reg1.each do |subkey, type, data|
212
+ if(subkey =~ /pathtoexe/i)
213
+ path_to_exe = data
214
+ end
215
+ end
216
+ end
217
+
218
+ puts "Starting Firefox using the executable : #{path_to_exe}"
219
+ puts "Waiting for #{waitTime} seconds for Firefox to get started."
220
+ @t = Thread.new { system("\"#{path_to_exe}\" -jssh #{profile_opt}") }
221
+ elsif(RUBY_PLATFORM =~ /linux/i)
222
+ puts RUBY_PLATFORM
223
+ path_to_bin = `which firefox`.strip
224
+ puts "#{path_to_bin} -jssh #{profile_opt}"
225
+ @t = Thread.new { `#{path_to_bin} -jssh #{profile_opt}` }
226
+ end
227
+
228
+ sleep waitTime
229
+
230
+ set_defaults()
231
+ get_window_number()
232
+ set_browser_document()
233
+ end
234
+
235
+ #
236
+ # Description:
237
+ # Creates a new instance of Firefox. Loads the URL and return the instance.
238
+ #
239
+ # Input:
240
+ # url - url of the page to be loaded.
241
+ #
242
+ # Output:
243
+ # New instance of firefox browser with the given url loaded.
244
+ #
245
+ def self.start(url)
246
+ ff = Firefox.new
247
+ ff.goto(url)
248
+ return ff
249
+ end
250
+
251
+ #
252
+ # Description:
253
+ # Gets the window number opened. Used internally by Firewatir.
254
+ #
255
+ def get_window_number()
256
+ $jssh_socket.send("getWindows().length;\n", 0)
257
+ @@current_window = read_socket().to_i - 1
258
+ # This will store the information about the window.
259
+ #@@window_stack.push(@@current_window)
260
+ #puts "here in get_window_number window number is #{@@current_window}"
261
+ return @@current_window
262
+ end
263
+ private :get_window_number
264
+
265
+ #
266
+ # Description:
267
+ # Loads the given url in the browser. Waits for the page to get loaded.
268
+ #
269
+ # Input:
270
+ # url - url to be loaded.
271
+ #
272
+ def goto(url)
273
+ #set_defaults()
274
+ get_window_number()
275
+ set_browser_document()
276
+ # Load the given url.
277
+ $jssh_socket.send("#{BROWSER_VAR}.loadURI(\"#{url}\");\n" , 0)
278
+ read_socket()
279
+
280
+ wait()
281
+ end
282
+
283
+ #
284
+ # Description:
285
+ # Loads the previous page (if there is any) in the browser. Waits for the page to get loaded.
286
+ #
287
+ def back()
288
+ #set_browser_document()
289
+ $jssh_socket.send("if(#{BROWSER_VAR}.canGoBack) #{BROWSER_VAR}.goBack();\n", 0)
290
+ read_socket();
291
+ wait()
292
+ end
293
+
294
+ #
295
+ # Description:
296
+ # Loads the next page (if there is any) in the browser. Waits for the page to get loaded.
297
+ #
298
+ def forward()
299
+ #set_browser_document()
300
+ $jssh_socket.send("if(#{BROWSER_VAR}.canGoForward) #{BROWSER_VAR}.goForward();\n", 0)
301
+ read_socket();
302
+ wait()
303
+ end
304
+
305
+ #
306
+ # Description:
307
+ # Reloads the current page in the browser. Waits for the page to get loaded.
308
+ #
309
+ def refresh()
310
+ #set_browser_document()
311
+ $jssh_socket.send("#{BROWSER_VAR}.reload();\n", 0)
312
+ read_socket();
313
+ wait()
314
+ end
315
+
316
+ #
317
+ # Description:
318
+ # This function creates a new socket at port 9997 and sets the default values for instance and class variables.
319
+ # Generatesi UnableToStartJSShException if cannot connect to jssh even after 3 tries.
320
+ #
321
+ def set_defaults(no_of_tries = 0)
322
+ # JSSH listens on port 9997. Create a new socket to connect to port 9997.
323
+ begin
324
+ $jssh_socket = TCPSocket::new(MACHINE_IP, "9997")
325
+ $jssh_socket.sync = true
326
+ read_socket()
327
+ rescue
328
+ no_of_tries += 1
329
+ retry if no_of_tries < 3
330
+ 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"
331
+ end
332
+ end
333
+ private :set_defaults
334
+
335
+ def set_slow_speed
336
+ @typingspeed = DEFAULT_TYPING_SPEED
337
+ @defaultSleepTime = DEFAULT_SLEEP_TIME
338
+ end
339
+ private :set_slow_speed
340
+
341
+ #
342
+ # Description:
343
+ # Sets the document, window and browser variables to point to correct object in JSSh.
344
+ #
345
+ def set_browser_document
346
+ # Get the window in variable WINDOW_VAR.
347
+ # Get the browser in variable BROWSER_VAR.
348
+ jssh_command = "var #{WINDOW_VAR} = getWindows()[#{@@current_window}];"
349
+ jssh_command += " var #{BROWSER_VAR} = #{WINDOW_VAR}.getBrowser();"
350
+ # Get the document and body in variable DOCUMENT_VAR and BODY_VAR respectively.
351
+ jssh_command += "var #{DOCUMENT_VAR} = #{BROWSER_VAR}.contentDocument;"
352
+ jssh_command += "var #{BODY_VAR} = #{DOCUMENT_VAR}.body;"
353
+
354
+ $jssh_socket.send("#{jssh_command}\n", 0)
355
+ read_socket()
356
+
357
+ # Get window and window's parent title and url
358
+ $jssh_socket.send("#{DOCUMENT_VAR}.title;\n", 0)
359
+ @window_title = read_socket()
360
+ $jssh_socket.send("#{DOCUMENT_VAR}.URL;\n", 0)
361
+ @window_url = read_socket()
362
+ end
363
+ private :set_browser_document
364
+
365
+ #
366
+ # Description:
367
+ # Closes the window.
368
+ #
369
+ def close()
370
+ #puts "current window number is : #{@@current_window}"
371
+ if @@current_window == 0
372
+ $jssh_socket.send(" getWindows()[0].close(); \n", 0)
373
+ @t.join if @t != nil
374
+ #sleep 5
375
+ else
376
+ # Check if window exists, because there may be the case that it has been closed by click event on some element.
377
+ # For e.g: Close Button, Close this Window link etc.
378
+ window_number = find_window("url", @window_url)
379
+
380
+ # If matching window found. Close the window.
381
+ if(window_number > 0)
382
+ $jssh_socket.send(" getWindows()[#{window_number}].close();\n", 0)
383
+ read_socket();
384
+ end
385
+
386
+ #Get the parent window url from the stack and return that window.
387
+ #@@current_window = @@window_stack.pop()
388
+ @window_url = @@window_stack.pop()
389
+ @window_title = @@window_stack.pop()
390
+ # Find window with this url.
391
+ window_number = find_window("url", @window_url)
392
+ @@current_window = window_number
393
+ set_browser_document()
394
+ end
395
+ end
396
+
397
+ #
398
+ # Description:
399
+ # Used for attaching pop up window to an existing Firefox window, either by url or title.
400
+ # ff.attach(:url, 'http://www.google.com')
401
+ # ff.attach(:title, 'Google')
402
+ #
403
+ # Output:
404
+ # Instance of newly attached window.
405
+ #
406
+ def attach(how, what)
407
+ window_number = find_window(how, what)
408
+
409
+ if(window_number == 0)
410
+ raise NoMatchingWindowFoundException.new("Unable to locate window, using #{how} and #{what}")
411
+ elsif(window_number > 0)
412
+ # Push the window_title and window_url of parent window. So that when we close the child window
413
+ # appropriate handle of parent window is returned back.
414
+ @@window_stack.push(@window_title)
415
+ @@window_stack.push(@window_url)
416
+
417
+ @@current_window = window_number.to_i
418
+ set_browser_document()
419
+ end
420
+ self
421
+ end
422
+
423
+ #
424
+ # Description:
425
+ # Finds a Firefox browser window with a given title or url.
426
+ #
427
+ def find_window(how, what)
428
+ jssh_command = "getWindows().length;";
429
+ $jssh_socket.send("#{jssh_command}\n", 0)
430
+ @@total_windows = read_socket()
431
+ #puts "total windows are : " + @@total_windows.to_s
432
+
433
+ jssh_command = "var windows = getWindows(); var window_number = 0;var found = false;
434
+ for(var i = 0; i < windows.length; i++)
435
+ {
436
+ var attribute = '';
437
+ if(\"#{how}\" == \"url\")
438
+ {
439
+ attribute = windows[i].getBrowser().contentDocument.URL;
440
+ }
441
+ if(\"#{how}\" == \"title\")
442
+ {
443
+ attribute = windows[i].getBrowser().contentDocument.title;
444
+ }"
445
+ if(what.class == Regexp)
446
+ # Construct the regular expression because we can't use it directly by converting it to string.
447
+ # If reg ex is /Google/i then its string conversion will be (?i-mx:Google) so we can't use it.
448
+ # Construct the regular expression again from the string conversion.
449
+ oldRegExp = what.to_s
450
+ newRegExp = "/" + what.source + "/"
451
+ flags = oldRegExp.slice(2, oldRegExp.index(':') - 2)
452
+
453
+ for i in 0..flags.length do
454
+ flag = flags[i, 1]
455
+ if(flag == '-')
456
+ break;
457
+ else
458
+ newRegExp << flag
459
+ end
460
+ end
461
+
462
+ jssh_command += "var regExp = new RegExp(#{newRegExp});
463
+ found = regExp.test(attribute);"
464
+ else
465
+ jssh_command += "found = (attribute == \"#{what}\");"
466
+ end
467
+
468
+ jssh_command += "if(found)
469
+ {
470
+ window_number = i;
471
+ break;
472
+ }
473
+ }
474
+ window_number;"
475
+
476
+ jssh_command.gsub!(/\n/, "")
477
+ #puts "jssh_command is : #{jssh_command}"
478
+ $jssh_socket.send("#{jssh_command}\n", 0)
479
+ window_number = read_socket()
480
+ #puts "window number is : " + window_number.to_s
481
+
482
+ return window_number.to_i
483
+ end
484
+ private :find_window
485
+
486
+ #
487
+ # Description:
488
+ # Matches the given text with the current text shown in the browser.
489
+ #
490
+ # Input:
491
+ # target - Text to match. Can be a string or regex
492
+ #
493
+ # Output:
494
+ # Returns the index if the specified text was found.
495
+ # Returns matchdata object if the specified regexp was found.
496
+ #
497
+ def contains_text(target)
498
+ #puts "Text to match is : #{match_text}"
499
+ #puts "Html is : #{self.text}"
500
+ if target.kind_of? Regexp
501
+ self.text.match(target)
502
+ elsif target.kind_of? String
503
+ self.text.index(target)
504
+ else
505
+ raise ArgumentError, "Argument #{target} should be a string or regexp."
506
+ end
507
+ end
508
+
509
+ #
510
+ # Description:
511
+ # Returns the url of the page currently loaded in the browser.
512
+ #
513
+ # Output:
514
+ # URL of the page.
515
+ #
516
+ def url()
517
+ @window_url
518
+ end
519
+
520
+ #
521
+ # Description:
522
+ # Returns the title of the page currently loaded in the browser.
523
+ #
524
+ # Output:
525
+ # Title of the page.
526
+ #
527
+ def title()
528
+ @window_title
529
+ end
530
+
531
+ #
532
+ # Description:
533
+ # Returns the html of the page currently loaded in the browser.
534
+ #
535
+ # Output:
536
+ # HTML shown on the page.
537
+ #
538
+ def html()
539
+ $jssh_socket.send("var htmlelem = #{DOCUMENT_VAR}.getElementsByTagName('html')[0]; htmlelem.innerHTML;\n", 0)
540
+ #$jssh_socket.send("#{BODY_VAR}.innerHTML;\n", 0)
541
+ result = read_socket()
542
+ return "<html>" + result + "</html>"
543
+ end
544
+
545
+ #
546
+ # Description:
547
+ # Returns the text of the page currently loaded in the browser.
548
+ #
549
+ # Output:
550
+ # Text shown on the page.
551
+ #
552
+ def text()
553
+ $jssh_socket.send("#{BODY_VAR}.textContent;\n", 0)
554
+ return read_socket().strip
555
+ end
556
+
557
+ #
558
+ # Description:
559
+ # Maximize the current browser window.
560
+ #
561
+ def maximize()
562
+ $jssh_socket.send("#{WINDOW_VAR}.maximize();\n", 0)
563
+ read_socket()
564
+ end
565
+
566
+ #
567
+ # Description:
568
+ # Minimize the current browser window.
569
+ #
570
+ def minimize()
571
+ $jssh_socket.send("#{WINDOW_VAR}.minimize();\n", 0)
572
+ read_socket()
573
+ end
574
+
575
+ #
576
+ # Description:
577
+ # Waits for the page to get loaded.
578
+ #
579
+ def wait()
580
+ #puts "In wait function "
581
+ isLoadingDocument = ""
582
+ while isLoadingDocument != "false"
583
+ $jssh_socket.send("#{BROWSER_VAR}=#{WINDOW_VAR}.getBrowser(); #{BROWSER_VAR}.webProgress.isLoadingDocument;\n" , 0)
584
+ isLoadingDocument = read_socket()
585
+ #puts "Is browser still loading page: #{isLoadingDocument}"
586
+ end
587
+
588
+ # Check for Javascript redirect. As we are connected to Firefox via JSSh. JSSh
589
+ # doesn't detect any javascript redirects so check it here.
590
+ # If page redirects to itself that this code will enter in infinite loop.
591
+ # So we currently don't wait for such a page.
592
+ # wait variable in JSSh tells if we should wait more for the page to get loaded
593
+ # or continue. -1 means page is not redirected. Anyother positive values means wait.
594
+ jssh_command = "var wait = -1; var meta = null; meta = #{BROWSER_VAR}.contentDocument.getElementsByTagName('meta');
595
+ if(meta != null)
596
+ {
597
+ var doc_url = #{BROWSER_VAR}.contentDocument.URL;
598
+ for(var i=0; i< meta.length;++i)
599
+ {
600
+ var content = meta[i].content;
601
+ var regex = new RegExp(\"^refresh$\", \"i\");
602
+ if(regex.test(meta[i].httpEquiv))
603
+ {
604
+ var arrContent = content.split(';');
605
+ var redirect_url = null;
606
+ if(arrContent.length > 0)
607
+ {
608
+ if(arrContent.length > 1)
609
+ redirect_url = arrContent[1];
610
+
611
+ if(redirect_url != null)
612
+ {
613
+ regex = new RegExp(\"^.*\" + redirect_url + \"$\");
614
+ if(!regex.test(doc_url))
615
+ {
616
+ wait = arrContent[0];
617
+ }
618
+ }
619
+ break;
620
+ }
621
+ }
622
+ }
623
+ }
624
+ wait;"
625
+ #puts "command in wait is : #{jssh_command}"
626
+ jssh_command = jssh_command.gsub(/\n/, "")
627
+ $jssh_socket.send("#{jssh_command}; \n", 0)
628
+ wait_time = read_socket();
629
+ #puts "wait time is : #{wait_time}"
630
+ begin
631
+ wait_time = wait_time.to_i
632
+ if(wait_time != -1)
633
+ sleep(wait_time)
634
+ # Call wait again. In case there are multiple redirects.
635
+ $jssh_socket.send("#{BROWSER_VAR} = #{WINDOW_VAR}.getBrowser(); \n",0)
636
+ read_socket()
637
+ wait()
638
+ end
639
+ rescue
640
+ end
641
+ set_browser_document()
642
+ end
643
+
644
+ #def jspopup_appeared(popupText = "", wait = 2)
645
+ # winHelper = WindowHelper.new()
646
+ # return winHelper.hasPopupAppeared(popupText, wait)
647
+ #end
648
+
649
+ #
650
+ # Description:
651
+ # Redefines the alert and confirm methods on the basis of button to be clicked.
652
+ # This is done so that JSSh doesn't get blocked. You should use click_no_wait method before calling this function.
653
+ #
654
+ # Typical Usage:
655
+ # ff.button(:id, "button").click_no_wait
656
+ # ff.click_jspopup_button("OK")
657
+ #
658
+ # Input:
659
+ # button - JavaScript button to be clicked. Values can be OK or Cancel
660
+ #
661
+ #def click_jspopup_button(button)
662
+ # button = button.downcase
663
+ # element = Element.new(nil)
664
+ # element.click_js_popup(button)
665
+ #end
666
+
667
+ #
668
+ # Description:
669
+ # Tells FireWatir to click javascript button in case one comes after performing some action on an element. Matches
670
+ # text of pop up with one if supplied as parameter. If text matches clicks the button else stop script execution until
671
+ # pop up is dismissed by manual intervention.
672
+ #
673
+ # Input:
674
+ # button - JavaScript button to be clicked. Values can be OK or Cancel
675
+ # waitTime - Time to wait for pop up to come. Not used just for compatibility with Watir.
676
+ # userInput - Not used just for compatibility with Watir
677
+ # text - Text that should appear on pop up.
678
+ #
679
+ def startClicker(button, waitTime = 1, userInput = nil, text = nil)
680
+ jssh_command = "var win = #{BROWSER_VAR}.contentWindow;"
681
+ if(button =~ /ok/i)
682
+ jssh_command += "var popuptext = '';
683
+ var old_alert = win.alert;
684
+ var old_confirm = win.confirm;
685
+ win.alert = function(param) {"
686
+ if(text != nil)
687
+ jssh_command += "if(param == \"#{text}\") {
688
+ popuptext = param;
689
+ return true;
690
+ }
691
+ else {
692
+ popuptext = param;
693
+ win.alert = old_alert;
694
+ win.alert(param);
695
+ }"
696
+ else
697
+ jssh_command += "popuptext = param; return true;"
698
+ end
699
+ jssh_command += "};
700
+ win.confirm = function(param) {"
701
+ if(text != nil)
702
+ jssh_command += "if(param == \"#{text}\") {
703
+ popuptext = param;
704
+ return true;
705
+ }
706
+ else {
707
+ win.confirm = old_confirm;
708
+ win.confirm(param);
709
+ }"
710
+ else
711
+ jssh_command += "popuptext = param; return true;"
712
+ end
713
+ jssh_command += "};"
714
+
715
+ elsif(button =~ /cancel/i)
716
+ jssh_command = "var old_confirm = win.confirm;
717
+ win.confirm = function(param) {"
718
+ if(text != nil)
719
+ jssh_command += "if(param == \"#{text}\") {
720
+ popuptext = param;
721
+ return false;
722
+ }
723
+ else {
724
+ win.confirm = old_confirm;
725
+ win.confirm(param);
726
+ }"
727
+ else
728
+ jssh_command += "popuptext = param; return false;"
729
+ end
730
+ jssh_command += "};"
731
+ end
732
+ jssh_command.gsub!(/\n/, "")
733
+ #puts "jssh command sent for js pop up is : #{jssh_command}"
734
+ $jssh_socket.send("#{jssh_command}\n", 0)
735
+ read_socket()
736
+ end
737
+
738
+ #
739
+ # Description:
740
+ # Returns text of javascript pop up in case it comes.
741
+ #
742
+ # Output:
743
+ # Text shown in javascript pop up.
744
+ #
745
+ def get_popup_text()
746
+ $jssh_socket.send("popuptext;\n", 0)
747
+ return_value = read_socket()
748
+ # reset the variable
749
+ $jssh_socket.send("popuptext = '';\n", 0)
750
+ read_socket()
751
+ return return_value
752
+ end
753
+
754
+ #
755
+ # Description:
756
+ # Returns the document element of the page currently loaded in the browser.
757
+ #
758
+ # Output:
759
+ # Document element.
760
+ #
761
+ def document
762
+ Document.new("#{DOCUMENT_VAR}")
763
+ end
764
+
765
+ #
766
+ # Description:
767
+ # Returns the first element that matches the xpath query.
768
+ #
769
+ # Input:
770
+ # Xpath expression or query.
771
+ #
772
+ # Output:
773
+ # Element matching the xpath query.
774
+ #
775
+ def element_by_xpath(xpath)
776
+ temp = Element.new(nil, self)
777
+ element_name = temp.element_by_xpath(self, xpath)
778
+ return element_factory(element_name)
779
+ end
780
+
781
+ #
782
+ # Description:
783
+ # Factory method to create object of correct Element class while using XPath to get the element.
784
+ #
785
+ def element_factory(element_name)
786
+ jssh_type = Element.new(element_name,self).element_type
787
+ #puts "jssh type is : #{jssh_type}" # DEBUG
788
+ candidate_class = jssh_type =~ /HTML(.*)Element/ ? $1 : ''
789
+ #puts candidate_class # DEBUG
790
+ if candidate_class == 'Input'
791
+ $jssh_socket.send("#{element_name}.type;\n", 0)
792
+ input_type = read_socket().downcase.strip
793
+ puts input_type # DEBUG
794
+ firewatir_class = input_class(input_type)
795
+ else
796
+ firewatir_class = jssh2firewatir(candidate_class)
797
+ end
798
+
799
+ #puts firewatir_class # DEBUG
800
+ klass = Kernel.const_get(firewatir_class)
801
+
802
+ if klass == Element
803
+ klass.new(element_name,self)
804
+ elsif klass == CheckBox
805
+ klass.new(self,:jssh_name,element_name,["checkbox"])
806
+ elsif klass == Radio
807
+ klass.new(self,:jssh_name,element_name,["radio"])
808
+ else
809
+ klass.new(self,:jssh_name,element_name)
810
+ end
811
+ end
812
+ private :element_factory
813
+
814
+ #
815
+ # Description:
816
+ # Get the class name for element of input type depending upon its type like checkbox, radio etc.
817
+ #
818
+ def input_class(input_type)
819
+ hash = {
820
+ 'select-one' => 'SelectList',
821
+ 'select-multiple' => 'SelectList',
822
+ 'text' => 'TextField',
823
+ 'password' => 'TextField',
824
+ 'textarea' => 'TextField',
825
+ # TODO when there's no type, it's a TextField
826
+ 'file' => 'FileField',
827
+ 'checkbox' => 'CheckBox',
828
+ 'radio' => 'Radio',
829
+ 'reset' => 'Button',
830
+ 'button' => 'Button',
831
+ 'submit' => 'Button',
832
+ 'image' => 'Button'
833
+ }
834
+ hash.default = 'Element'
835
+
836
+ hash[input_type]
837
+ end
838
+ private :input_class
839
+
840
+ #
841
+ # Description:
842
+ # Converts element type returned by JSSh like HTMLDivElement to its corresponding class in Firewatir.
843
+ #
844
+ def jssh2firewatir(candidate_class)
845
+ hash = {
846
+ 'Div' => 'Div',
847
+ 'Button' => 'Button',
848
+ 'Frame' => 'Frame',
849
+ 'Span' => 'Span',
850
+ 'Paragraph' => 'P',
851
+ 'Label' => 'Label',
852
+ 'Form' => 'Form',
853
+ 'Image' => 'Image',
854
+ 'Table' => 'Table',
855
+ 'TableCell' => 'TableCell',
856
+ 'TableRow' => 'TableRow',
857
+ 'Select' => 'SelectList',
858
+ 'Link' => 'Link',
859
+ 'Anchor' => 'Link' # FIXME is this right?
860
+ #'Option' => 'Option' #Option uses a different constructor
861
+ }
862
+ hash.default = 'Element'
863
+ hash[candidate_class]
864
+ end
865
+ private :jssh2firewatir
866
+
867
+ #
868
+ # Description:
869
+ # Returns the array of elements that matches the xpath query.
870
+ #
871
+ # Input:
872
+ # Xpath expression or query.
873
+ #
874
+ # Output:
875
+ # Array of elements matching xpath query.
876
+ #
877
+ def elements_by_xpath(xpath)
878
+ element = Element.new(nil, self)
879
+ elem_names = element.elements_by_xpath(self, xpath)
880
+ a = elem_names.inject([]) {|elements,name| elements << element_factory(name)}
881
+ end
882
+
883
+ #
884
+ # Description:
885
+ # Show all the forms available on the page.
886
+ #
887
+ # Output:
888
+ # Name, id, method and action of all the forms available on the page.
889
+ #
890
+ def show_forms
891
+ forms = Document.new(self).get_forms()
892
+ count = forms.length
893
+ puts "There are #{count} forms"
894
+ for i in 0..count - 1 do
895
+ puts "Form name: " + forms[i].name
896
+ puts " id: " + forms[i].id
897
+ puts " method: " + forms[i].attribute_value("method")
898
+ puts " action: " + forms[i].action
899
+ end
900
+ end
901
+ alias showForms show_forms
902
+
903
+ #
904
+ # Description:
905
+ # Show all the images available on the page.
906
+ #
907
+ # Output:
908
+ # Name, id, src and index of all the images available on the page.
909
+ #
910
+ def show_images
911
+ images = Document.new(self).get_images
912
+ puts "There are #{images.length} images"
913
+ index = 1
914
+ images.each do |l|
915
+ puts "image: name: #{l.name}"
916
+ puts " id: #{l.id}"
917
+ puts " src: #{l.src}"
918
+ puts " index: #{index}"
919
+ index += 1
920
+ end
921
+ end
922
+ alias showImages show_images
923
+
924
+ #
925
+ # Description:
926
+ # Show all the links available on the page.
927
+ #
928
+ # Output:
929
+ # Name, id, href and index of all the links available on the page.
930
+ #
931
+ def show_links
932
+ links = Document.new(self).get_links
933
+ puts "There are #{links.length} links"
934
+ index = 1
935
+ links.each do |l|
936
+ puts "link: name: #{l.name}"
937
+ puts " id: #{l.id}"
938
+ puts " href: #{l.href}"
939
+ puts " index: #{index}"
940
+ index += 1
941
+ end
942
+ end
943
+ alias showLinks show_links
944
+
945
+ #
946
+ # Description:
947
+ # Show all the divs available on the page.
948
+ #
949
+ # Output:
950
+ # Name, id, class and index of all the divs available on the page.
951
+ #
952
+ def show_divs
953
+ divs = Document.new(self).get_divs
954
+ puts "There are #{divs.length} divs"
955
+ index = 1
956
+ divs.each do |l|
957
+ puts "div: name: #{l.name}"
958
+ puts " id: #{l.id}"
959
+ puts " class: #{l.className}"
960
+ puts " index: #{index}"
961
+ index += 1
962
+ end
963
+ end
964
+ alias showDivs show_divs
965
+
966
+ #
967
+ # Description:
968
+ # Show all the tables available on the page.
969
+ #
970
+ # Output:
971
+ # Id, row count, column count (only first row) and index of all the tables available on the page.
972
+ #
973
+ def show_tables
974
+ tables = Document.new(self).get_tables
975
+ puts "There are #{tables.length} tables"
976
+ index = 1
977
+ tables.each do |l|
978
+ puts "table: id: #{l.id}"
979
+ puts " rows: #{l.row_count}"
980
+ puts " columns: #{l.column_count}"
981
+ puts " index: #{index}"
982
+ index += 1
983
+ end
984
+ end
985
+ alias showTables show_tables
986
+
987
+ #
988
+ # Description:
989
+ # Show all the pre elements available on the page.
990
+ #
991
+ # Output:
992
+ # Id, name and index of all the pre elements available on the page.
993
+ #
994
+ def show_pres
995
+ pres = Document.new(self).get_pres
996
+ puts "There are #{pres.length} pres"
997
+ index = 1
998
+ pres.each do |l|
999
+ puts "pre: id: #{l.id}"
1000
+ puts " name: #{l.name}"
1001
+ puts " index: #{index}"
1002
+ index += 1
1003
+ end
1004
+ end
1005
+ alias showPres show_pres
1006
+
1007
+ #
1008
+ # Description:
1009
+ # Show all the spans available on the page.
1010
+ #
1011
+ # Output:
1012
+ # Name, id, class and index of all the spans available on the page.
1013
+ #
1014
+ def show_spans
1015
+ spans = Document.new(self).get_spans
1016
+ puts "There are #{spans.length} spans"
1017
+ index = 1
1018
+ spans.each do |l|
1019
+ puts "span: name: #{l.name}"
1020
+ puts " id: #{l.id}"
1021
+ puts " class: #{l.className}"
1022
+ puts " index: #{index}"
1023
+ index += 1
1024
+ end
1025
+ end
1026
+ alias showSpans show_spans
1027
+
1028
+ #
1029
+ # Description:
1030
+ # Show all the labels available on the page.
1031
+ #
1032
+ # Output:
1033
+ # Name, id, for and index of all the labels available on the page.
1034
+ #
1035
+ def show_labels
1036
+ labels = Document.new(self).get_labels
1037
+ puts "There are #{labels.length} labels"
1038
+ index = 1
1039
+ labels.each do |l|
1040
+ puts "label: name: #{l.name}"
1041
+ puts " id: #{l.id}"
1042
+ puts " for: #{l.for}"
1043
+ puts " index: #{index}"
1044
+ index += 1
1045
+ end
1046
+ end
1047
+ alias showLabels show_labels
1048
+
1049
+ #
1050
+ # Description:
1051
+ # Show all the frames available on the page. Doesn't show nested frames.
1052
+ #
1053
+ # Output:
1054
+ # Name, and index of all the frames available on the page.
1055
+ #
1056
+ def show_frames
1057
+ jssh_command = "var frameset = #{WINDOW_VAR}.frames;
1058
+ var elements_frames = new Array();
1059
+ for(var i = 0; i < frameset.length; i++)
1060
+ {
1061
+ var frames = frameset[i].frames;
1062
+ for(var j = 0; j < frames.length; j++)
1063
+ {
1064
+ elements_frames.push(frames[j].frameElement);
1065
+ }
1066
+ }
1067
+ elements_frames.length;"
1068
+
1069
+ jssh_command.gsub!("\n", "")
1070
+ $jssh_socket.send("#{jssh_command};\n", 0)
1071
+ length = read_socket().to_i
1072
+
1073
+ puts "There are #{length} frames"
1074
+
1075
+ frames = Array.new(length)
1076
+ for i in 0..length - 1 do
1077
+ frames[i] = Frame.new(self, :jssh_name, "elements_frames[#{i}]")
1078
+ end
1079
+
1080
+ for i in 0..length - 1 do
1081
+ puts "frame: name: #{frames[i].name}"
1082
+ puts " index: #{i+1}"
1083
+ end
1084
+ end
1085
+ alias showFrames show_frames
1086
+
1087
+ end # Class Firefox
1088
+
1089
+ #
1090
+ # Module for handling the Javascript pop-ups. Not in use currently, will be available in future.
1091
+ # Use ff.startClicker() method for clicking javascript pop ups. Refer to unit tests on how to handle
1092
+ # javascript pop up (unittests/javascript_test.rb)
1093
+ #module Dialog
1094
+ # # Class for handling javascript popup. Not in use currently, will be available in future. See unit tests on how to handle
1095
+ # # javascript pop up (unittests/javascript_test.rb).
1096
+ # class JSPopUp
1097
+ # include Container
1098
+ #
1099
+ # def has_appeared(text)
1100
+ # require 'socket'
1101
+ # sleep 4
1102
+ # shell = TCPSocket.new("localhost", 9997)
1103
+ # read_socket(shell)
1104
+ # #jssh_command = "var url = #{DOCUMENT_VAR}.URL;"
1105
+ # jssh_command = "var length = getWindows().length; var win;length;\n"
1106
+ # #jssh_command += "for(var i = 0; i < length; i++)"
1107
+ # #jssh_command += "{"
1108
+ # #jssh_command += " win = getWindows()[i];"
1109
+ # #jssh_command += " if(win.opener != null && "
1110
+ # #jssh_command += " win.title == \"[JavaScript Application]\" &&"
1111
+ # #jssh_command += " win.opener.document.URL == url)"
1112
+ # #jssh_command += " {"
1113
+ # #jssh_command += " break;"
1114
+ # #jssh_command += " }"
1115
+ # #jssh_command += "}"
1116
+ #
1117
+ # #jssh_command += " win.title;\n";
1118
+ # #jssh_command += "var dialog = win.document.childNodes[0];"
1119
+ # #jssh_command += "vbox = dialog.childNodes[1].childNodes[1];"
1120
+ # #jssh_command += "vbox.childNodes[1].childNodes[0].childNodes[0].textContent;\n"
1121
+ # puts jssh_command
1122
+ # shell.send("#{jssh_command}", 0)
1123
+ # jstext = read_socket(shell)
1124
+ # puts jstext
1125
+ # return jstext == text
1126
+ # end
1127
+ # end
1128
+ #end
1129
+
1130
+ end