firewatir 1.2.1 → 1.6.2

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