vapir-ie 1.7.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,9 @@
1
+ require 'vapir-ie/element'
2
+
3
+ require 'vapir-ie/frame'
4
+ require 'vapir-ie/form'
5
+ require 'vapir-ie/non_control_elements'
6
+ require 'vapir-ie/input_elements'
7
+ require 'vapir-ie/table'
8
+ require 'vapir-ie/image'
9
+ require 'vapir-ie/link'
@@ -0,0 +1,8 @@
1
+ require 'vapir-ie/element'
2
+ require 'vapir-common/elements/elements'
3
+
4
+ module Vapir
5
+ class IE::Form < IE::Element
6
+ include Vapir::Form
7
+ end # class IE::Form
8
+ end
@@ -0,0 +1,24 @@
1
+ require 'vapir-ie/element'
2
+ require 'vapir-common/elements/elements'
3
+ require 'vapir-ie/page_container'
4
+
5
+ module Vapir
6
+ class IE::Frame < IE::Element
7
+ include Frame
8
+ include IE::PageContainer
9
+
10
+ def content_window_object
11
+ element_object.contentWindow
12
+ end
13
+
14
+ def document_object
15
+ content_window_object.document
16
+ end
17
+ alias document document_object
18
+
19
+ def attach_command
20
+ @container.page_container.attach_command + ".frame(#{@how.inspect}, #{@what.inspect})"
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,880 @@
1
+ require 'vapir-ie/container'
2
+ require 'vapir-ie/page_container'
3
+ require 'vapir-ie/close_all'
4
+ require 'vapir-ie/modal_dialog'
5
+ require 'vapir-ie/win32ole'
6
+ require 'vapir-ie/ie-process'
7
+ require 'vapir-ie/logger'
8
+
9
+ module Vapir
10
+ class IE < Browser
11
+ include Vapir::Exception
12
+ include IE::PageContainer
13
+
14
+ # Maximum number of seconds to wait when attaching to a window
15
+ @@attach_timeout = 2.0 # default value
16
+ def self.attach_timeout
17
+ @@attach_timeout
18
+ end
19
+ def self.attach_timeout=(timeout)
20
+ @@attach_timeout = timeout
21
+ end
22
+
23
+ # Return the options used when creating new instances of IE.
24
+ # BUG: this interface invites misunderstanding/misuse such as IE.options[:speed] = :zippy]
25
+ def self.options
26
+ {:speed => self.speed, :visible => self.visible, :attach_timeout => self.attach_timeout}
27
+ end
28
+ # set values for options used when creating new instances of IE.
29
+ def self.set_options options
30
+ options.each do |name, value|
31
+ send "#{name}=", value
32
+ end
33
+ end
34
+ # The globals $FAST_SPEED and $HIDE_IE are checked both at initialization
35
+ # and later, because they
36
+ # might be set after initialization. Setting them beforehand (e.g. from
37
+ # the command line) will affect the class, otherwise it is only a temporary
38
+ # effect
39
+ @@speed = $FAST_SPEED ? :fast : :slow
40
+ def self.speed
41
+ return :fast if $FAST_SPEED
42
+ @@speed
43
+ end
44
+ def self.speed= x
45
+ $FAST_SPEED = nil
46
+ @@speed = x
47
+ end
48
+ @@visible = $HIDE_IE ? false : true
49
+ def self.visible
50
+ return false if $HIDE_IE
51
+ @@visible
52
+ end
53
+ def self.visible= x
54
+ $HIDE_IE = nil
55
+ @@visible = x
56
+ end
57
+
58
+ # IE inserts some element whose tagName is empty and just acts as block level element
59
+ # Probably some IE method of cleaning things
60
+ # To pass the same to the xml parser we need to give some name to empty tagName
61
+ EMPTY_TAG_NAME = "DUMMY"
62
+
63
+ # The time, in seconds, it took for the new page to load after executing the
64
+ # the last command
65
+ attr_reader :down_load_time
66
+
67
+ # the OLE Internet Explorer object
68
+ attr_accessor :ie
69
+
70
+ # access to the logger object
71
+ attr_accessor :logger
72
+
73
+ # this contains the list of unique urls that have been visited
74
+ attr_reader :url_list
75
+
76
+ # Create a new IE window. Works just like IE.new in Watir 1.4.
77
+ def self.new_window
78
+ ie = new true
79
+ ie._new_window_init
80
+ ie
81
+ end
82
+
83
+ # Create an IE browser.
84
+ def initialize suppress_new_window=nil
85
+ _new_window_init unless suppress_new_window
86
+ end
87
+
88
+ def _new_window_init
89
+ create_browser_window
90
+ initialize_options
91
+ goto 'about:blank' # this avoids numerous problems caused by lack of a document
92
+ end
93
+
94
+ # Create a new IE Window, starting at the specified url.
95
+ # If no url is given, start empty.
96
+ def self.start url=nil
97
+ start_window url
98
+ end
99
+
100
+ # Create a new IE window, starting at the specified url.
101
+ # If no url is given, start empty. Works like IE.start in Watir 1.4.
102
+ def self.start_window url=nil
103
+ ie = new_window
104
+ ie.goto url if url
105
+ ie
106
+ end
107
+
108
+ # Create a new IE window in a new process.
109
+ # This method will not work when
110
+ # Vapir/Ruby is run under a service (instead of a user).
111
+ def self.new_process
112
+ ie = new true
113
+ ie._new_process_init
114
+ ie
115
+ end
116
+
117
+ def _new_process_init
118
+ iep = Process.start
119
+ @ie = iep.window
120
+ @process_id = iep.process_id
121
+ initialize_options
122
+ goto 'about:blank'
123
+ end
124
+
125
+ # Create a new IE window in a new process, starting at the specified URL.
126
+ # Same as IE.start.
127
+ def self.start_process url=nil
128
+ ie = new_process
129
+ ie.goto url if url
130
+ ie
131
+ end
132
+
133
+ # Return a Vapir::IE object for an existing IE window. Window can be
134
+ # referenced by url, title, or window handle.
135
+ # Second argument can be either a string or a regular expression in the
136
+ # case of of :url or :title.
137
+ # IE.attach(:url, 'http://www.google.com')
138
+ # IE.attach(:title, 'Google')
139
+ # IE.attach(:hwnd, 528140)
140
+ # This method will not work when
141
+ # Vapir/Ruby is run under a service (instead of a user).
142
+ def self.attach how, what
143
+ ie = new true # don't create window
144
+ ie._attach_init(how, what)
145
+ ie
146
+ end
147
+
148
+ # this method is used internally to attach to an existing window
149
+ def _attach_init how, what
150
+ attach_browser_window how, what
151
+ initialize_options
152
+ wait
153
+ end
154
+
155
+ # Return an IE object that wraps the given window, typically obtained from
156
+ # Shell.Application.windows.
157
+ def self.bind window
158
+ ie = new true
159
+ ie.ie = window
160
+ ie.initialize_options
161
+ ie
162
+ end
163
+
164
+ def create_browser_window
165
+ @ie = WIN32OLE.new('InternetExplorer.Application')
166
+ end
167
+ private :create_browser_window
168
+
169
+ def initialize_options
170
+ self.visible = IE.visible
171
+ self.speed = IE.speed
172
+
173
+ @element_object = nil
174
+ @page_container = self
175
+ @error_checkers = []
176
+
177
+ @logger = DefaultLogger.new
178
+ @url_list = []
179
+ end
180
+
181
+ # Specifies the speed that commands will be executed at. Choices are:
182
+ # * :slow (default)
183
+ # * :fast
184
+ # * :zippy
185
+ # With IE#speed= :zippy, text fields will be entered at once, instead of
186
+ # character by character (default).
187
+ def speed= how_fast
188
+ case how_fast
189
+ when :zippy then
190
+ @typingspeed = 0
191
+ @pause_after_wait = 0.01
192
+ @type_keys = false
193
+ @speed = :fast
194
+ when :fast then
195
+ @typingspeed = 0
196
+ @pause_after_wait = 0.01
197
+ @type_keys = true
198
+ @speed = :fast
199
+ when :slow then
200
+ @typingspeed = 0.08
201
+ @pause_after_wait = 0.1
202
+ @type_keys = true
203
+ @speed = :slow
204
+ else
205
+ raise ArgumentError, "Invalid speed: #{how_fast}"
206
+ end
207
+ end
208
+
209
+ def speed
210
+ return @speed if @speed == :slow
211
+ return @type_keys ? :fast : :zippy
212
+ end
213
+
214
+ # deprecated: use speed = :fast instead
215
+ def set_fast_speed
216
+ self.speed = :fast
217
+ end
218
+
219
+ # deprecated: use speed = :slow instead
220
+ def set_slow_speed
221
+ self.speed = :slow
222
+ end
223
+
224
+ def visible
225
+ assert_exists
226
+ @ie.visible
227
+ end
228
+ def visible=(boolean)
229
+ assert_exists
230
+ @ie.visible = boolean if boolean != @ie.visible
231
+ end
232
+
233
+ # Yields successively to each IE window on the current desktop. Takes a block.
234
+ # This method will not work when
235
+ # Vapir/Ruby is run under a service (instead of a user).
236
+ # Yields to the window and its hwnd.
237
+ def self.each
238
+ shell = WIN32OLE.new('Shell.Application')
239
+ shell.Windows.each do |window|
240
+ next unless (window.path =~ /Internet Explorer/ rescue false)
241
+ next unless (hwnd = window.hwnd rescue false)
242
+ ie = IE.bind(window)
243
+ ie.hwnd = hwnd
244
+ yield ie
245
+ end
246
+ end
247
+
248
+ # return internet explorer instance as specified. if none is found,
249
+ # return nil.
250
+ # arguments:
251
+ # :url, url -- the URL of the IE browser window
252
+ # :title, title -- the title of the browser page
253
+ # :hwnd, hwnd -- the window handle of the browser window.
254
+ # This method will not work when
255
+ # Vapir/Ruby is run under a service (instead of a user).
256
+ def self.find(how, what)
257
+ ie_ole = IE._find(how, what)
258
+ IE.bind ie_ole if ie_ole
259
+ end
260
+
261
+ def self._find(how, what)
262
+ ieTemp = nil
263
+ IE.each do |ie|
264
+ window = ie.ie
265
+
266
+ case how
267
+ when :url
268
+ ieTemp = window if Vapir::fuzzy_match(window.locationURL, what)
269
+ when :title
270
+ # normal windows explorer shells do not have document
271
+ # note window.document will fail for "new" browsers
272
+ begin
273
+ title = window.locationname
274
+ title = window.document.title
275
+ rescue WIN32OLERuntimeError
276
+ end
277
+ ieTemp = window if Vapir::fuzzy_match(title, what)
278
+ when :hwnd
279
+ begin
280
+ ieTemp = window if what == window.HWND
281
+ rescue WIN32OLERuntimeError
282
+ end
283
+ else
284
+ raise ArgumentError
285
+ end
286
+ end
287
+ return ieTemp
288
+ end
289
+
290
+ def attach_browser_window how, what
291
+ log "Seeking Window with #{how}: #{what}"
292
+ ieTemp = nil
293
+ begin
294
+ Vapir::until_with_timeout do
295
+ ieTemp = IE._find how, what
296
+ end
297
+ rescue TimeOutException
298
+ raise NoMatchingWindowFoundException,
299
+ "Unable to locate a window with #{how} of #{what}"
300
+ end
301
+ @ie = ieTemp
302
+ end
303
+ private :attach_browser_window
304
+
305
+ def browser_object
306
+ assert_exists
307
+ @ie
308
+ end
309
+
310
+ # Return the current window handle
311
+ def hwnd
312
+ assert_exists
313
+ @hwnd ||= @ie.hwnd
314
+ end
315
+ attr_writer :hwnd
316
+
317
+ def win_window
318
+ @win_window||= WinWindow.new(hwnd)
319
+ end
320
+
321
+ def modal_dialog(options={})
322
+ assert_exists do
323
+ raise ArgumentError, "options argument must be a hash; received #{options.inspect} (#{options.class})" unless options.is_a?(Hash)
324
+ modal=IE::ModalDialog.new(self, options.merge(:error => false))
325
+ modal.exists? ? modal : nil
326
+ end
327
+ end
328
+
329
+ def modal_dialog!(options={})
330
+ assert_exists do
331
+ IE::ModalDialog.new(self, options.merge(:error => true))
332
+ end
333
+ end
334
+
335
+ # we expect one of these error codes when quitting or checking existence.
336
+ ExistenceFailureCodesRE = Regexp.new(
337
+ { '0x800706ba' => 'The RPC server is unavailable',
338
+ '0x80010108' => 'The object invoked has disconnected from its clients.',
339
+ '0x800706be' => 'The remote procedure call failed.',
340
+ }.keys.join('|'), Regexp::IGNORECASE)
341
+
342
+ # Are we attached to an open browser?
343
+ def exists?
344
+ !!(@ie && begin
345
+ @ie.name
346
+ rescue WIN32OLERuntimeError
347
+ raise unless $!.message =~ ExistenceFailureCodesRE
348
+ false
349
+ end)
350
+ end
351
+ alias :exist? :exists?
352
+
353
+ # deprecated: use logger= instead
354
+ def set_logger(logger)
355
+ @logger = logger
356
+ end
357
+
358
+ def log(what)
359
+ @logger.debug(what) if @logger
360
+ end
361
+
362
+ #
363
+ # Accessing data outside the document
364
+ #
365
+
366
+ # Return the title of the document
367
+ def title
368
+ @ie.document.title
369
+ end
370
+
371
+ # Return the status of the window, typically from the status bar at the bottom.
372
+ def status
373
+ return @ie.statusText
374
+ end
375
+
376
+ #
377
+ # Navigation
378
+ #
379
+
380
+ # Navigate to the specified URL.
381
+ # * url - string - the URL to navigate to
382
+ def goto(url)
383
+ assert_exists do
384
+ @ie.navigate(url)
385
+ wait
386
+ return @down_load_time
387
+ end
388
+ end
389
+
390
+ # Go to the previous page - the same as clicking the browsers back button
391
+ # an WIN32OLERuntimeError exception is raised if the browser cant go back
392
+ def back
393
+ assert_exists do
394
+ @ie.GoBack
395
+ wait
396
+ end
397
+ end
398
+
399
+ # Go to the next page - the same as clicking the browsers forward button
400
+ # an WIN32OLERuntimeError exception is raised if the browser cant go forward
401
+ def forward
402
+ assert_exists do
403
+ @ie.GoForward
404
+ wait
405
+ end
406
+ end
407
+
408
+ module RefreshConstants
409
+ # http://msdn.microsoft.com/en-us/library/bb268230%28v=VS.85%29.aspx
410
+ REFRESH_NORMAL = 0
411
+ REFRESH_IFEXPIRED = 1
412
+ REFRESH_COMPLETELY = 3
413
+ end
414
+ # Refresh the current page - the same as clicking the browsers refresh button
415
+ # an WIN32OLERuntimeError exception is raised if the browser cant refresh
416
+ def refresh
417
+ assert_exists do
418
+ @ie.refresh2(RefreshConstants::REFRESH_COMPLETELY)
419
+ wait
420
+ end
421
+ end
422
+
423
+ # clear the list of urls that we have visited
424
+ def clear_url_list
425
+ @url_list.clear
426
+ end
427
+
428
+ # Closes the Browser
429
+ def close
430
+ assert_exists
431
+ @ie.stop
432
+ @ie.quit
433
+ # TODO/fix timeout; this shouldn't be a hard-coded magic number.
434
+ ::Waiter.try_for(32, :exception => WindowFailedToCloseException.new("The browser window did not close"), :interval => 1) do
435
+ begin
436
+ if exists?
437
+ @ie.quit
438
+ false
439
+ else
440
+ true
441
+ end
442
+ rescue WIN32OLERuntimeError
443
+ raise unless $!.message =~ ExistenceFailureCodesRE
444
+ true
445
+ end
446
+ end
447
+ @ie=nil
448
+ end
449
+
450
+ # Maximize the window (expands to fill the screen)
451
+ def maximize
452
+ win_window.maximize!
453
+ end
454
+
455
+ # Minimize the window (appears as icon on taskbar)
456
+ def minimize
457
+ win_window.minimize!
458
+ end
459
+
460
+ # Restore the window (after minimizing or maximizing)
461
+ def restore
462
+ win_window.restore!
463
+ end
464
+
465
+ # Make the window come to the front
466
+ def bring_to_front
467
+ win_window.really_set_foreground!
468
+ end
469
+
470
+ def front?
471
+ win_window.foreground?
472
+ end
473
+
474
+ # Send key events to IE window.
475
+ # See http://www.autoitscript.com/autoit3/docs/appendix/SendKeys.htm
476
+ # for complete documentation on keys supported and syntax.
477
+ def send_keys(key_string)
478
+ assert_exists do
479
+ require 'vapir-ie/autoit'
480
+ bring_to_front
481
+ Vapir.autoit.Send key_string
482
+ end
483
+ end
484
+
485
+ # saves a screenshot of this browser window to the given filename.
486
+ #
487
+ # second argument, optional, specifies what area to take a screenshot of.
488
+ # - :client takes a screenshot of the client area, which excludes the menu bar and other window trimmings.
489
+ # - :window (default) takes a screenshot of the full browser window
490
+ # - :desktop takes a screenshot of the full desktop
491
+ def screen_capture(filename, dc=:window)
492
+ if dc==:desktop
493
+ screenshot_win=WinWindow.desktop_window
494
+ dc=:window
495
+ else
496
+ screenshot_win=win_window
497
+ end
498
+ screenshot_win.capture_to_bmp_file(filename, :dc => dc)
499
+ end
500
+
501
+ def dir
502
+ return File.expand_path(File.dirname(__FILE__))
503
+ end
504
+
505
+ #
506
+ # Document and Document Data
507
+ #
508
+
509
+ # Return the current document
510
+ def document
511
+ assert_exists
512
+ return @ie.document
513
+ end
514
+ alias document_object document
515
+
516
+ def browser
517
+ self
518
+ end
519
+
520
+ # returns the current url, as displayed in the address bar of the browser
521
+ def url
522
+ assert_exists
523
+ return @ie.LocationURL
524
+ end
525
+
526
+ # Error checkers
527
+
528
+ # this method runs the predefined error checks
529
+ def run_error_checks
530
+ assert_exists do
531
+ @error_checkers.each { |e| e.call(self) }
532
+ end
533
+ end
534
+
535
+ # this method is used to add an error checker that gets executed on every page load
536
+ # * checker Proc Object, that contains the code to be run
537
+ def add_checker(checker)
538
+ @error_checkers << checker
539
+ end
540
+
541
+ # this allows a checker to be disabled
542
+ # * checker Proc Object, the checker that is to be disabled
543
+ def disable_checker(checker)
544
+ @error_checkers.delete(checker)
545
+ end
546
+
547
+ # this method shows the name, id etc of the object that is currently active - ie the element that has focus
548
+ # its mostly used in irb when creating a script
549
+ def show_active # TODO/fix: move to common; test
550
+
551
+ current_object = document.activeElement
552
+ current_element = base_class.factory(current_object)
553
+ current_element.to_s
554
+ end
555
+
556
+ # Gives focus to the frame
557
+ def focus
558
+ document.activeElement.blur
559
+ document.focus
560
+ end
561
+
562
+
563
+ # Functions written for using xpath for getting the elements.
564
+ def xmlparser_document_object
565
+ if @xml_parser_doc == nil
566
+ create_xml_parser_doc
567
+ end
568
+ return @xml_parser_doc
569
+ end
570
+
571
+ # Create the Nokogiri object if it is nil. This method is private so can be called only
572
+ # from xmlparser_document_object method.
573
+ def create_xml_parser_doc
574
+ require 'nokogiri'
575
+ if @xml_parser_doc == nil
576
+ htmlSource ="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<HTML>\n"
577
+ htmlSource = html_source(document.body,htmlSource," ")
578
+ htmlSource += "\n</HTML>\n"
579
+ # Angrez: Resolving Jira issue WTR-114
580
+ htmlSource = htmlSource.gsub(/&nbsp;/, '&#160;')
581
+ begin
582
+ #@xml_parser_doc = Nokogiri::HTML::Document.new(htmlSource)
583
+ @xml_parser_doc = Nokogiri.parse(htmlSource)
584
+ rescue => e
585
+ output_xml_parser_doc("error.xml", htmlSource)
586
+ raise e
587
+ end
588
+ end
589
+ end
590
+ private :create_xml_parser_doc
591
+
592
+ def output_xml_parser_doc(name, text)
593
+ file = File.open(name,"w")
594
+ file.print(text)
595
+ file.close
596
+ end
597
+ private :output_xml_parser_doc
598
+
599
+ #Function Tokenizes the tag line and returns array of tokens.
600
+ #Token could be either tagName or "=" or attribute name or attribute value
601
+ #Attribute value could be either quoted string or single word
602
+ def tokenize_tagline(outerHtml)
603
+ outerHtml = outerHtml.gsub(/\n|\r/," ")
604
+ #removing "< symbol", opening of current tag
605
+ outerHtml =~ /^\s*<(.*)$/
606
+ outerHtml = $1
607
+ tokens = Array.new
608
+ i = startOffset = 0
609
+ length = outerHtml.length
610
+ #puts outerHtml
611
+ parsingValue = false
612
+ while i < length do
613
+ i +=1 while (i < length && outerHtml[i,1] =~ /\s/)
614
+ next if i == length
615
+ currentToken = outerHtml[i,1]
616
+
617
+ #Either current tag has been closed or user has not closed the tag >
618
+ # and we have received the opening of next element
619
+ break if currentToken =~ /<|>/
620
+
621
+ #parse quoted value
622
+ if(currentToken == "\"" || currentToken == "'")
623
+ parsingValue = false
624
+ quote = currentToken
625
+ startOffset = i
626
+ i += 1
627
+ i += 1 while (i < length && (outerHtml[i,1] != quote || outerHtml[i-1,1] == "\\"))
628
+ if i == length
629
+ tokens.push quote + outerHtml[startOffset..i-1]
630
+ else
631
+ tokens.push outerHtml[startOffset..i]
632
+ end
633
+ elsif currentToken == "="
634
+ tokens.push "="
635
+ parsingValue = true
636
+ else
637
+ startOffset = i
638
+ i += 1 while (i < length && !(outerHtml[i,1] =~ /\s|=|<|>/)) if !parsingValue
639
+ i += 1 while (i < length && !(outerHtml[i,1] =~ /\s|<|>/)) if parsingValue
640
+ parsingValue = false
641
+ i -= 1
642
+ tokens.push outerHtml[startOffset..i]
643
+ end
644
+ i += 1
645
+ end
646
+ return tokens
647
+ end
648
+ private :tokenize_tagline
649
+
650
+ # This function get and clean all the attributes of the tag.
651
+ def all_tag_attributes(outerHtml)
652
+ tokens = tokenize_tagline(outerHtml)
653
+ #puts tokens
654
+ tagLine = ""
655
+ count = 1
656
+ tokensLength = tokens.length
657
+ expectedEqualityOP= false
658
+ while count < tokensLength do
659
+ if expectedEqualityOP == false
660
+ #print Attribute Name
661
+ # If attribute name is valid. Refer: http://www.w3.org/TR/REC-xml/#NT-Name
662
+ if tokens[count] =~ /^(\w|_|:)(.*)$/
663
+ tagLine += " #{tokens[count]}"
664
+ expectedEqualityOP = true
665
+ end
666
+ elsif tokens[count] == "="
667
+ count += 1
668
+ if count == tokensLength
669
+ tagLine += "=\"\""
670
+ elsif(tokens[count][0,1] == "\"" || tokens[count][0,1] == "'")
671
+ tagLine += "=#{tokens[count]}"
672
+ else
673
+ tagLine += "=\"#{tokens[count]}\""
674
+ end
675
+ expectedEqualityOP = false
676
+ else
677
+ #Opps! equality was expected but its not there.
678
+ #Set value same as the attribute name e.g. selected="selected"
679
+ tagLine += "=\"#{tokens[count-1]}\""
680
+ expectedEqualityOP = false
681
+ next
682
+ end
683
+ count += 1
684
+ end
685
+ tagLine += "=\"#{tokens[count-1]}\" " if expectedEqualityOP == true
686
+ #puts tagLine
687
+ return tagLine
688
+ end
689
+ private :all_tag_attributes
690
+
691
+ # This function is used to escape the characters that are not valid XML data.
692
+ def xml_escape(str)
693
+ str = str.gsub(/&/,'&amp;')
694
+ str = str.gsub(/</,'&lt;')
695
+ str = str.gsub(/>/,'&gt;')
696
+ str = str.gsub(/"/, '&quot;')
697
+ str
698
+ end
699
+ private :xml_escape
700
+
701
+ # Returns HTML Source
702
+ # Traverse the DOM tree rooted at body element
703
+ # and generate the HTML source.
704
+ # element: Represent Current element
705
+ # htmlString:HTML Source
706
+ # spaces:(Used for debugging). Helps in indentation
707
+ def html_source(element, htmlString, spaceString)
708
+ begin
709
+ tagLine = ""
710
+ outerHtml = ""
711
+ tagName = ""
712
+ begin
713
+ tagName = element.tagName.downcase
714
+ tagName = EMPTY_TAG_NAME if tagName == ""
715
+ # If tag is a mismatched tag.
716
+ if !(tagName =~ /^(\w|_|:)(.*)$/)
717
+ return htmlString
718
+ end
719
+ rescue
720
+ #handling text nodes
721
+ htmlString += xml_escape(element.toString)
722
+ return htmlString
723
+ end
724
+ #puts tagName
725
+ #Skip comment and script tag
726
+ if tagName =~ /^!/ || tagName== "script" || tagName =="style"
727
+ return htmlString
728
+ end
729
+ #tagLine += spaceString
730
+ outerHtml = all_tag_attributes(element.outerHtml) if tagName != EMPTY_TAG_NAME
731
+ tagLine += "<#{tagName} #{outerHtml}"
732
+
733
+ canHaveChildren = element.canHaveChildren
734
+ if canHaveChildren
735
+ tagLine += ">"
736
+ else
737
+ tagLine += "/>" #self closing tag
738
+ end
739
+ #spaceString += spaceString
740
+ htmlString += tagLine
741
+ childElements = element.childnodes
742
+ childElements.each do |child|
743
+ htmlString = html_source(child,htmlString,spaceString)
744
+ end
745
+ if canHaveChildren
746
+ #tagLine += spaceString
747
+ tagLine ="</" + tagName + ">"
748
+ htmlString += tagLine
749
+ end
750
+ return htmlString
751
+ rescue => e
752
+ puts e.to_s
753
+ end
754
+ return htmlString
755
+ end
756
+ private :html_source
757
+
758
+ public
759
+ # return the first element object (not Element) that matches the xpath
760
+ def element_object_by_xpath(xpath)
761
+ objects= element_objects_by_xpath(xpath)
762
+ return (objects && objects[0])
763
+ end
764
+
765
+ # execute xpath and return an array of elements
766
+ def element_objects_by_xpath(xpath)
767
+ doc = xmlparser_document_object
768
+ modifiedXpath = ""
769
+ selectedElements = Array.new
770
+
771
+ # strip any trailing slash from the xpath expression (as used in watir unit tests)
772
+ xpath.chop! unless (/\/$/ =~ xpath).nil?
773
+
774
+ doc.xpath(xpath).each do |element|
775
+ modifiedXpath = element.path
776
+ temp = element_by_absolute_xpath(modifiedXpath) # temp = a DOM/COM element
777
+ selectedElements << temp if temp != nil
778
+ end
779
+ #puts selectedElements.length
780
+ if selectedElements.length == 0
781
+ return nil
782
+ else
783
+ return selectedElements
784
+ end
785
+ end
786
+
787
+ # Method that iterates over IE DOM object and get the elements for the given
788
+ # xpath.
789
+ def element_by_absolute_xpath(xpath)
790
+ curElem = nil
791
+
792
+ #puts "Hello; Given xpath is : #{xpath}"
793
+ doc = document
794
+ curElem = doc.getElementsByTagName("body").item(0)
795
+ xpath =~ /^.*\/body\[?\d*\]?\/(.*)/
796
+ xpath = $1
797
+
798
+ if xpath == nil
799
+ puts "Function Requires absolute XPath."
800
+ return
801
+ end
802
+
803
+ arr = xpath.split(/\//)
804
+ return nil if arr.length == 0
805
+
806
+ lastTagName = arr[arr.length-1].to_s.upcase
807
+
808
+ # lastTagName is like tagName[number] or just tagName. For the first case we need to
809
+ # separate tagName and number.
810
+ lastTagName =~ /(\w*)\[?\d*\]?/
811
+ lastTagName = $1
812
+ #puts lastTagName
813
+
814
+ for element in arr do
815
+ element =~ /(\w*)\[?(\d*)\]?/
816
+ tagname = $1
817
+ tagname = tagname.upcase
818
+
819
+ if $2 != nil && $2 != ""
820
+ index = $2
821
+ index = "#{index}".to_i - 1
822
+ else
823
+ index = 0
824
+ end
825
+
826
+ #puts "#{element} #{tagname} #{index}"
827
+ allElemns = curElem.childnodes
828
+ if allElemns == nil || allElemns.length == 0
829
+ puts "#{element} is null"
830
+ next # Go to next element
831
+ end
832
+
833
+ #puts "Current element is : #{curElem.tagName}"
834
+ allElemns.each do |child|
835
+ gotIt = false
836
+ begin
837
+ curTag = child.tagName
838
+ curTag = EMPTY_TAG_NAME if curTag == ""
839
+ rescue
840
+ next
841
+ end
842
+ #puts child.tagName
843
+ if curTag == tagname
844
+ index-=1
845
+ if index < 0
846
+ curElem = child
847
+ break
848
+ end
849
+ end
850
+ end
851
+
852
+ #puts "Node selected at index #{index.to_s} : #{curElem.tagName}"
853
+ end
854
+ begin
855
+ if curElem.tagName == lastTagName
856
+ #puts curElem.tagName
857
+ return curElem
858
+ else
859
+ return nil
860
+ end
861
+ rescue
862
+ return nil
863
+ end
864
+ end
865
+ private :element_by_absolute_xpath
866
+
867
+ def attach_command
868
+ "Vapir::IE.attach(:hwnd, #{hwnd})"
869
+ end
870
+
871
+ private
872
+ def base_element_class
873
+ IE::Element
874
+ end
875
+ def browser_class
876
+ IE
877
+ end
878
+
879
+ end # class IE
880
+ end