watir 1.5.2 → 1.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,60 @@
1
+ module Watir
2
+ class Frame
3
+ include Container
4
+ include PageContainer
5
+
6
+ # Find the frame denoted by how and what in the container and return its ole_object
7
+ def locate
8
+ how = @how
9
+ what = @what
10
+ frames = @container.document.frames
11
+ target = nil
12
+
13
+ for i in 0..(frames.length - 1)
14
+ this_frame = frames.item(i)
15
+ case how
16
+ when :index
17
+ index = i + 1
18
+ return this_frame if index == what
19
+ when :name
20
+ begin
21
+ return this_frame if what.matches(this_frame.name)
22
+ rescue # access denied?
23
+ end
24
+ when :id
25
+ # We assume that pages contain frames or iframes, but not both.
26
+ this_frame_tag = @container.document.getElementsByTagName("FRAME").item(i)
27
+ return this_frame if this_frame_tag and what.matches(this_frame_tag.invoke("id"))
28
+ this_iframe_tag = @container.document.getElementsByTagName("IFRAME").item(i)
29
+ return this_frame if this_iframe_tag and what.matches(this_iframe_tag.invoke("id"))
30
+ when :src
31
+ this_frame_tag = @container.document.getElementsByTagName("FRAME").item(i)
32
+ return this_frame if this_frame_tag and what.matches(this_frame_tag.src)
33
+ this_iframe_tag = @container.document.getElementsByTagName("IFRAME").item(i)
34
+ return this_frame if this_iframe_tag and what.matches(this_iframe_tag.src)
35
+ else
36
+ raise ArgumentError, "Argument #{how} not supported"
37
+ end
38
+ end
39
+
40
+ raise UnknownFrameException, "Unable to locate a frame with #{how.to_s} #{what}"
41
+ end
42
+
43
+ def initialize(container, how, what)
44
+ set_container container
45
+ @how = how
46
+ @what = what
47
+ @o = locate
48
+ copy_test_config container
49
+ end
50
+
51
+ def document
52
+ @o.document
53
+ end
54
+
55
+ def attach_command
56
+ @container.page_container.attach_command + ".frame(#{@how.inspect}, #{@what.inspect})"
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,973 @@
1
+ module Watir
2
+ class IE
3
+ include Watir::Exception
4
+ include Container
5
+ include PageContainer
6
+
7
+ def self.quit
8
+ end
9
+
10
+ # Maximum number of seconds to wait when attaching to a window
11
+ def self.reset_attach_timeout
12
+ @@attach_timeout = 2.0
13
+ end
14
+ reset_attach_timeout
15
+ def self.attach_timeout
16
+ @@attach_timeout
17
+ end
18
+ def self.attach_timeout=(timeout)
19
+ @@attach_timeout = timeout
20
+ end
21
+
22
+ # The revision number (according to Subversion)
23
+ REVISION_STRING = '$Revision: 1263 $'
24
+ REVISION_STRING.scan(/Revision: (\d*)/)
25
+ REVISION = $1 or 'unknown'
26
+
27
+ # The Release number
28
+ VERSION_SHORT = '1.5.3'
29
+ VERSION = VERSION_SHORT + '.' + REVISION
30
+
31
+ # Used internally to determine when IE has finished loading a page
32
+ READYSTATE_COMPLETE = 4
33
+
34
+ # TODO: the following constants should be able to be specified by object (not class)
35
+
36
+ # The delay when entering text on a web page when speed = :slow.
37
+ DEFAULT_TYPING_SPEED = 0.08
38
+
39
+ # The default time we wait after a page has loaded when speed = :slow.
40
+ DEFAULT_SLEEP_TIME = 0.1
41
+
42
+ # The default color for highlighting objects as they are accessed.
43
+ HIGHLIGHT_COLOR = 'yellow'
44
+
45
+ # IE inserts some element whose tagName is empty and just acts as block level element
46
+ # Probably some IE method of cleaning things
47
+ # To pass the same to REXML we need to give some name to empty tagName
48
+ EMPTY_TAG_NAME = "DUMMY"
49
+
50
+ # The time, in seconds, it took for the new page to load after executing the
51
+ # the last command
52
+ attr_reader :down_load_time
53
+
54
+ # Whether the speed is :fast or :slow
55
+ attr_reader :speed
56
+
57
+ # the OLE Internet Explorer object
58
+ attr_accessor :ie
59
+
60
+ # access to the logger object
61
+ attr_accessor :logger
62
+
63
+ # this contains the list of unique urls that have been visited
64
+ attr_reader :url_list
65
+
66
+ # Create a new IE window. Works just like IE.new in Watir 1.4.
67
+ def self.new_window
68
+ ie = new true
69
+ ie._new_window_init
70
+ ie
71
+ end
72
+
73
+ # Create an IE browser.
74
+ def initialize suppress_new_window=nil
75
+ _new_window_init unless suppress_new_window
76
+ end
77
+
78
+ def _new_window_init
79
+ create_browser_window
80
+ set_defaults
81
+ goto 'about:blank' # this avoids numerous problems caused by lack of a document
82
+ end
83
+
84
+ # Create a new IE Window, starting at the specified url.
85
+ # If no url is given, start empty.
86
+ def self.start url=nil
87
+ start_window url
88
+ end
89
+
90
+ # Create a new IE window, starting at the specified url.
91
+ # If no url is given, start empty. Works like IE.start in Watir 1.4.
92
+ def self.start_window url=nil
93
+ ie = new_window
94
+ ie.goto url if url
95
+ ie
96
+ end
97
+
98
+ # Create a new IE window in a new process.
99
+ # This method will not work when
100
+ # Watir/Ruby is run under a service (instead of a user).
101
+ def self.new_process
102
+ ie = new true
103
+ ie._new_process_init
104
+ ie
105
+ end
106
+
107
+ def _new_process_init
108
+ iep = Process.start
109
+ @ie = iep.window
110
+ @process_id = iep.process_id
111
+ set_defaults
112
+ goto 'about:blank'
113
+ end
114
+
115
+ # Create a new IE window in a new process, starting at the specified URL.
116
+ # Same as IE.start.
117
+ def self.start_process url=nil
118
+ ie = new_process
119
+ ie.goto url if url
120
+ ie
121
+ end
122
+
123
+ # Return a Watir::IE object for an existing IE window. Window can be
124
+ # referenced by url, title, or window handle.
125
+ # Second argument can be either a string or a regular expression in the
126
+ # case of of :url or :title.
127
+ # IE.attach(:url, 'http://www.google.com')
128
+ # IE.attach(:title, 'Google')
129
+ # IE.attach(:hwnd, 528140)
130
+ # This method will not work when
131
+ # Watir/Ruby is run under a service (instead of a user).
132
+ def self.attach how, what
133
+ ie = new true # don't create window
134
+ ie._attach_init(how, what)
135
+ ie
136
+ end
137
+
138
+ # this method is used internally to attach to an existing window
139
+ def _attach_init how, what
140
+ attach_browser_window how, what
141
+ set_defaults
142
+ wait
143
+ end
144
+
145
+ # Return an IE object that wraps the given window, typically obtained from
146
+ # Shell.Application.windows.
147
+ def self.bind window
148
+ ie = new true
149
+ ie.ie = window
150
+ ie.set_defaults
151
+ ie
152
+ end
153
+
154
+ def create_browser_window
155
+ @ie = WIN32OLE.new('InternetExplorer.Application')
156
+ end
157
+ private :create_browser_window
158
+
159
+ def set_defaults
160
+ self.visible = ! $HIDE_IE
161
+ @ole_object = nil
162
+ @page_container = self
163
+ @error_checkers = []
164
+ @activeObjectHighLightColor = HIGHLIGHT_COLOR
165
+
166
+ if $FAST_SPEED
167
+ set_fast_speed
168
+ else
169
+ set_slow_speed
170
+ end
171
+
172
+ @logger = DefaultLogger.new
173
+ @url_list = []
174
+ end
175
+
176
+ def speed= how_fast
177
+ case how_fast
178
+ when :fast : set_fast_speed
179
+ when :slow : set_slow_speed
180
+ else
181
+ raise ArgumentError, "Invalid speed: #{how_fast}"
182
+ end
183
+ end
184
+
185
+ # deprecated: use speed = :fast instead
186
+ def set_fast_speed
187
+ @typingspeed = 0
188
+ @defaultSleepTime = 0.01
189
+ @speed = :fast
190
+ end
191
+
192
+ # deprecated: use speed = :slow instead
193
+ def set_slow_speed
194
+ @typingspeed = DEFAULT_TYPING_SPEED
195
+ @defaultSleepTime = DEFAULT_SLEEP_TIME
196
+ @speed = :slow
197
+ end
198
+
199
+ def visible
200
+ @ie.visible
201
+ end
202
+ def visible=(boolean)
203
+ @ie.visible = boolean if boolean != @ie.visible
204
+ end
205
+
206
+ # Yields successively to each IE window on the current desktop. Takes a block.
207
+ # This method will not work when
208
+ # Watir/Ruby is run under a service (instead of a user).
209
+ # Yields to the window and its hwnd.
210
+ def self.each
211
+ shell = WIN32OLE.new('Shell.Application')
212
+ shell.Windows.each do |window|
213
+ next unless (window.path =~ /Internet Explorer/ rescue false)
214
+ next unless (hwnd = window.hwnd rescue false)
215
+ ie = IE.bind(window)
216
+ ie.hwnd = hwnd
217
+ yield ie
218
+ end
219
+ end
220
+
221
+ # return internet explorer instance as specified. if none is found,
222
+ # return nil.
223
+ # arguments:
224
+ # :url, url -- the URL of the IE browser window
225
+ # :title, title -- the title of the browser page
226
+ # :hwnd, hwnd -- the window handle of the browser window.
227
+ # This method will not work when
228
+ # Watir/Ruby is run under a service (instead of a user).
229
+ def self.find(how, what)
230
+ ie_ole = IE._find(how, what)
231
+ IE.bind ie_ole if ie_ole
232
+ end
233
+
234
+ def self._find(how, what)
235
+ ieTemp = nil
236
+ IE.each do |ie|
237
+ window = ie.ie
238
+
239
+ case how
240
+ when :url
241
+ ieTemp = window if (what.matches(window.locationURL))
242
+ when :title
243
+ # normal windows explorer shells do not have document
244
+ # note window.document will fail for "new" browsers
245
+ begin
246
+ title = window.locationname
247
+ title = window.document.title
248
+ rescue WIN32OLERuntimeError
249
+ end
250
+ ieTemp = window if what.matches(title)
251
+ when :hwnd
252
+ begin
253
+ ieTemp = window if what == window.HWND
254
+ rescue WIN32OLERuntimeError
255
+ end
256
+ else
257
+ raise ArgumentError
258
+ end
259
+ end
260
+ return ieTemp
261
+ end
262
+
263
+ def attach_browser_window how, what
264
+ log "Seeking Window with #{how}: #{what}"
265
+ ieTemp = nil
266
+ begin
267
+ Watir::until_with_timeout do
268
+ ieTemp = IE._find how, what
269
+ end
270
+ rescue TimeOutException
271
+ raise NoMatchingWindowFoundException,
272
+ "Unable to locate a window with #{how} of #{what}"
273
+ end
274
+ @ie = ieTemp
275
+ end
276
+ private :attach_browser_window
277
+
278
+ # Return the current window handle
279
+ def hwnd
280
+ raise "Not attached to a browser" if @ie.nil?
281
+ @hwnd ||= @ie.hwnd
282
+ end
283
+ attr_writer :hwnd
284
+
285
+ include Watir::Win32
286
+
287
+ # Are we attached to an open browser?
288
+ def exists?
289
+ return false if @closing
290
+ begin
291
+ @ie.name =~ /Internet Explorer/
292
+ rescue WIN32OLERuntimeError
293
+ false
294
+ end
295
+ end
296
+ alias :exist? :exists?
297
+
298
+ # deprecated: use logger= instead
299
+ def set_logger(logger)
300
+ @logger = logger
301
+ end
302
+
303
+ def log(what)
304
+ @logger.debug(what) if @logger
305
+ end
306
+
307
+ #
308
+ # Accessing data outside the document
309
+ #
310
+
311
+ # Return the title of the document
312
+ def title
313
+ @ie.document.title
314
+ end
315
+
316
+ # Return the status of the window, typically from the status bar at the bottom.
317
+ def status
318
+ raise NoStatusBarException if !@ie.statusBar
319
+ return @ie.statusText
320
+ end
321
+
322
+ #
323
+ # Navigation
324
+ #
325
+
326
+ # Navigate to the specified URL.
327
+ # * url - string - the URL to navigate to
328
+ def goto(url)
329
+ @ie.navigate(url)
330
+ wait
331
+ return @down_load_time
332
+ end
333
+
334
+ # Go to the previous page - the same as clicking the browsers back button
335
+ # an WIN32OLERuntimeError exception is raised if the browser cant go back
336
+ def back
337
+ @ie.GoBack
338
+ wait
339
+ end
340
+
341
+ # Go to the next page - the same as clicking the browsers forward button
342
+ # an WIN32OLERuntimeError exception is raised if the browser cant go forward
343
+ def forward
344
+ @ie.GoForward
345
+ wait
346
+ end
347
+
348
+ # Refresh the current page - the same as clicking the browsers refresh button
349
+ # an WIN32OLERuntimeError exception is raised if the browser cant refresh
350
+ def refresh
351
+ @ie.refresh2(3)
352
+ wait
353
+ end
354
+
355
+ # clear the list of urls that we have visited
356
+ def clear_url_list
357
+ @url_list.clear
358
+ end
359
+
360
+ # Closes the Browser
361
+ def close
362
+ @closing = true
363
+ @ie.quit
364
+ end
365
+
366
+ # Maximize the window (expands to fill the screen)
367
+ def maximize
368
+ set_window_state :SW_MAXIMIZE
369
+ end
370
+
371
+ # Minimize the window (appears as icon on taskbar)
372
+ def minimize
373
+ set_window_state :SW_MINIMIZE
374
+ end
375
+
376
+ # Restore the window (after minimizing or maximizing)
377
+ def restore
378
+ set_window_state :SW_RESTORE
379
+ end
380
+
381
+ # Make the window come to the front
382
+ def bring_to_front
383
+ autoit.WinActivate title, ''
384
+ end
385
+
386
+ def front?
387
+ 1 == autoit.WinActive(title, '')
388
+ end
389
+
390
+ private
391
+ def set_window_state(state)
392
+ autoit.WinSetState title, '', autoit.send(state)
393
+ end
394
+ def autoit
395
+ Watir::autoit
396
+ end
397
+ public
398
+
399
+ # Send key events to IE window.
400
+ # See http://www.autoitscript.com/autoit3/docs/appendix/SendKeys.htm
401
+ # for complete documentation on keys supported and syntax.
402
+ def send_keys(key_string)
403
+ autoit.WinActivate title
404
+ autoit.Send key_string
405
+ end
406
+
407
+ def dir
408
+ return File.expand_path(File.dirname(__FILE__))
409
+ end
410
+
411
+ #
412
+ # Document and Document Data
413
+ #
414
+
415
+ # Return the current document
416
+ def document
417
+ return @ie.document
418
+ end
419
+
420
+ # returns the current url, as displayed in the address bar of the browser
421
+ def url
422
+ return @ie.LocationURL
423
+ end
424
+
425
+ #
426
+ # Synchronization
427
+ #
428
+ include Watir::Utils
429
+
430
+ # Block execution until the page has loaded.
431
+ # =nodoc
432
+ # Note: This code needs to be prepared for the ie object to be closed at
433
+ # any moment!
434
+ def wait(no_sleep=false)
435
+ @rexmlDomobject = nil
436
+ @down_load_time = 0.0
437
+ a_moment = 0.2 # seconds
438
+ start_load_time = Time.now
439
+
440
+ begin
441
+ while @ie.busy # XXX need to add time out
442
+ sleep a_moment
443
+ end
444
+ until @ie.readyState == READYSTATE_COMPLETE do
445
+ sleep a_moment
446
+ end
447
+ sleep a_moment
448
+ until @ie.document do
449
+ sleep a_moment
450
+ end
451
+
452
+ documents_to_wait_for = [@ie.document]
453
+
454
+ rescue WIN32OLERuntimeError # IE window must have been closed
455
+ @down_load_time = Time.now - start_load_time
456
+ sleep @defaultSleepTime unless no_sleep
457
+ return @down_load_time
458
+ end
459
+
460
+ while doc = documents_to_wait_for.shift
461
+ begin
462
+ until doc.readyState == "complete" do
463
+ sleep a_moment
464
+ end
465
+ @url_list << doc.url unless @url_list.include?(doc.url)
466
+ doc.frames.length.times do |n|
467
+ begin
468
+ documents_to_wait_for << doc.frames[n.to_s].document
469
+ rescue WIN32OLERuntimeError
470
+ end
471
+ end
472
+ rescue WIN32OLERuntimeError
473
+ end
474
+ end
475
+
476
+ @down_load_time = Time.now - start_load_time
477
+ run_error_checks
478
+ sleep @defaultSleepTime unless no_sleep
479
+ @down_load_time
480
+ end
481
+
482
+ # Error checkers
483
+
484
+ # this method runs the predefined error checks
485
+ def run_error_checks
486
+ @error_checkers.each { |e| e.call(self) }
487
+ end
488
+
489
+ # this method is used to add an error checker that gets executed on every page load
490
+ # * checker Proc Object, that contains the code to be run
491
+ def add_checker(checker)
492
+ @error_checkers << checker
493
+ end
494
+
495
+ # this allows a checker to be disabled
496
+ # * checker Proc Object, the checker that is to be disabled
497
+ def disable_checker(checker)
498
+ @error_checkers.delete(checker)
499
+ end
500
+
501
+ #
502
+ # Show me state
503
+ #
504
+
505
+ # Show all forms displays all the forms that are on a web page.
506
+ def show_forms
507
+ if allForms = document.forms
508
+ count = allForms.length
509
+ puts "There are #{count} forms"
510
+ for i in 0..count-1 do
511
+ wrapped = FormWrapper.new(allForms.item(i))
512
+ puts "Form name: #{wrapped.name}"
513
+ puts " id: #{wrapped.id}"
514
+ puts " method: #{wrapped.method}"
515
+ puts " action: #{wrapped.action}"
516
+ end
517
+ else
518
+ puts "No forms"
519
+ end
520
+ end
521
+
522
+ # this method shows all the images availble in the document
523
+ def show_images
524
+ doc = document
525
+ index = 1
526
+ doc.images.each do |l|
527
+ puts "image: name: #{l.name}"
528
+ puts " id: #{l.invoke("id")}"
529
+ puts " src: #{l.src}"
530
+ puts " index: #{index}"
531
+ index += 1
532
+ end
533
+ end
534
+
535
+ # this method shows all the links availble in the document
536
+ def show_links
537
+ props = ["name", "id", "href"]
538
+ print_sizes = [12, 12, 60]
539
+ doc = document
540
+ index = 0
541
+ text_size = 60
542
+ # draw the table header
543
+ s = "index".ljust(6)
544
+ props.each_with_index do |p, i|
545
+ s += p.ljust(print_sizes[i])
546
+ end
547
+ s += "text/src".ljust(text_size)
548
+ s += "\n"
549
+
550
+ # now get the details of the links
551
+ doc.links.each do |n|
552
+ index += 1
553
+ s = s + index.to_s.ljust(6)
554
+ props.each_with_index do |prop, i|
555
+ printsize = print_sizes[i]
556
+ begin
557
+ p = n.invoke(prop)
558
+ temp_var = "#{p}".to_s.ljust(printsize)
559
+ rescue
560
+ # this object probably doesnt have this property
561
+ temp_var = "".to_s.ljust(printsize)
562
+ end
563
+ s += temp_var
564
+ end
565
+ s += n.innerText
566
+ if n.getElementsByTagName("IMG").length > 0
567
+ s += " / " + n.getElementsByTagName("IMG")[0.to_s].src
568
+ end
569
+ s += "\n"
570
+ end
571
+ puts s
572
+ end
573
+
574
+ # this method shows the name, id etc of the object that is currently active - ie the element that has focus
575
+ # its mostly used in irb when creating a script
576
+ def show_active
577
+ s = ""
578
+
579
+ current = document.activeElement
580
+ begin
581
+ s += current.invoke("type").to_s.ljust(16)
582
+ rescue
583
+ end
584
+ props = ["name", "id", "value", "alt", "src", "innerText", "href"]
585
+ props.each do |prop|
586
+ begin
587
+ p = current.invoke(prop)
588
+ s += " " + "#{prop}=#{p}".to_s.ljust(18)
589
+ rescue
590
+ #this object probably doesnt have this property
591
+ end
592
+ end
593
+ s += "\n"
594
+ end
595
+
596
+ # this method shows all the divs availble in the document
597
+ def show_divs
598
+ divs = document.getElementsByTagName("DIV")
599
+ puts "Found #{divs.length} div tags"
600
+ index = 1
601
+ divs.each do |d|
602
+ puts "#{index} id=#{d.invoke('id')} class=#{d.invoke("className")}"
603
+ index += 1
604
+ end
605
+ end
606
+
607
+ # this method is used to show all the tables that are available
608
+ def show_tables
609
+ tables = document.getElementsByTagName("TABLE")
610
+ puts "Found #{tables.length} tables"
611
+ index = 1
612
+ tables.each do |d|
613
+ puts "#{index} id=#{d.invoke('id')} rows=#{d.rows.length} columns=#{begin d.rows["0"].cells.length; rescue; end}"
614
+ index += 1
615
+ end
616
+ end
617
+
618
+ def show_pres
619
+ pres = document.getElementsByTagName("PRE")
620
+ puts "Found #{ pres.length } pre tags"
621
+ index = 1
622
+ pres.each do |d|
623
+ puts "#{index} id=#{d.invoke('id')} class=#{d.invoke("className")}"
624
+ index+=1
625
+ end
626
+ end
627
+
628
+ # this method shows all the spans availble in the document
629
+ def show_spans
630
+ spans = document.getElementsByTagName("SPAN")
631
+ puts "Found #{spans.length} span tags"
632
+ index = 1
633
+ spans.each do |d|
634
+ puts "#{index} id=#{d.invoke('id')} class=#{d.invoke("className")}"
635
+ index += 1
636
+ end
637
+ end
638
+
639
+ def show_labels
640
+ labels = document.getElementsByTagName("LABEL")
641
+ puts "Found #{labels.length} label tags"
642
+ index = 1
643
+ labels.each do |d|
644
+ puts "#{index} text=#{d.invoke('innerText')} class=#{d.invoke("className")} for=#{d.invoke("htmlFor")}"
645
+ index += 1
646
+ end
647
+ end
648
+
649
+ # Gives focus to the frame
650
+ def focus
651
+ document.activeElement.blur
652
+ document.focus
653
+ end
654
+
655
+ #
656
+ # Functions written for using xpath for getting the elements.
657
+ #
658
+
659
+ # Get the Rexml object.
660
+ def rexml_document_object
661
+ #puts "Value of rexmlDomobject is : #{@rexmlDomobject}"
662
+ if @rexmlDomobject == nil
663
+ create_rexml_document_object
664
+ end
665
+ return @rexmlDomobject
666
+ end
667
+
668
+ # Create the Rexml object if it is nil. This method is private so can be called only
669
+ # from rexml_document_object method.
670
+ def create_rexml_document_object
671
+ # Use our modified rexml libraries
672
+ require 'rexml/document'
673
+ unless REXML::Version >= '3.1.4'
674
+ raise "Requires REXML version of at least 3.1.4. Actual: #{REXML::Version}"
675
+ end
676
+ if @rexmlDomobject == nil
677
+ htmlSource ="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<HTML>\n"
678
+ htmlSource = html_source(document.body,htmlSource," ")
679
+ htmlSource += "\n</HTML>\n"
680
+ # Angrez: Resolving Jira issue WTR-114
681
+ htmlSource = htmlSource.gsub(/&nbsp;/, '&#160;')
682
+ begin
683
+ @rexmlDomobject = REXML::Document.new(htmlSource)
684
+ rescue => e
685
+ output_rexml_document("error.xml", htmlSource)
686
+ raise e
687
+ end
688
+ end
689
+ end
690
+ private :create_rexml_document_object
691
+
692
+ def output_rexml_document(name, text)
693
+ file = File.open(name,"w")
694
+ file.print(text)
695
+ file.close
696
+ end
697
+ private :output_rexml_document
698
+
699
+ #Function Tokenizes the tag line and returns array of tokens.
700
+ #Token could be either tagName or "=" or attribute name or attribute value
701
+ #Attribute value could be either quoted string or single word
702
+ def tokenize_tagline(outerHtml)
703
+ outerHtml = outerHtml.gsub(/\n|\r/," ")
704
+ #removing "< symbol", opening of current tag
705
+ outerHtml =~ /^\s*<(.*)$/
706
+ outerHtml = $1
707
+ tokens = Array.new
708
+ i = startOffset = 0
709
+ length = outerHtml.length
710
+ #puts outerHtml
711
+ parsingValue = false
712
+ while i < length do
713
+ i +=1 while (i < length && outerHtml[i,1] =~ /\s/)
714
+ next if i == length
715
+ currentToken = outerHtml[i,1]
716
+
717
+ #Either current tag has been closed or user has not closed the tag >
718
+ # and we have received the opening of next element
719
+ break if currentToken =~ /<|>/
720
+
721
+ #parse quoted value
722
+ if(currentToken == "\"" || currentToken == "'")
723
+ parsingValue = false
724
+ quote = currentToken
725
+ startOffset = i
726
+ i += 1
727
+ i += 1 while (i < length && (outerHtml[i,1] != quote || outerHtml[i-1,1] == "\\"))
728
+ if i == length
729
+ tokens.push quote + outerHtml[startOffset..i-1]
730
+ else
731
+ tokens.push outerHtml[startOffset..i]
732
+ end
733
+ elsif currentToken == "="
734
+ tokens.push "="
735
+ parsingValue = true
736
+ else
737
+ startOffset = i
738
+ i += 1 while (i < length && !(outerHtml[i,1] =~ /\s|=|<|>/)) if !parsingValue
739
+ i += 1 while (i < length && !(outerHtml[i,1] =~ /\s|<|>/)) if parsingValue
740
+ parsingValue = false
741
+ i -= 1
742
+ tokens.push outerHtml[startOffset..i]
743
+ end
744
+ i += 1
745
+ end
746
+ return tokens
747
+ end
748
+ private :tokenize_tagline
749
+
750
+ # This function get and clean all the attributes of the tag.
751
+ def all_tag_attributes(outerHtml)
752
+ tokens = tokenize_tagline(outerHtml)
753
+ #puts tokens
754
+ tagLine = ""
755
+ count = 1
756
+ tokensLength = tokens.length
757
+ expectedEqualityOP= false
758
+ while count < tokensLength do
759
+ if expectedEqualityOP == false
760
+ #print Attribute Name
761
+ # If attribute name is valid. Refer: http://www.w3.org/TR/REC-xml/#NT-Name
762
+ if tokens[count] =~ /^(\w|_|:)(.*)$/
763
+ tagLine += " #{tokens[count]}"
764
+ expectedEqualityOP = true
765
+ end
766
+ elsif tokens[count] == "="
767
+ count += 1
768
+ if count == tokensLength
769
+ tagLine += "=\"\""
770
+ elsif(tokens[count][0,1] == "\"" || tokens[count][0,1] == "'")
771
+ tagLine += "=#{tokens[count]}"
772
+ else
773
+ tagLine += "=\"#{tokens[count]}\""
774
+ end
775
+ expectedEqualityOP = false
776
+ else
777
+ #Opps! equality was expected but its not there.
778
+ #Set value same as the attribute name e.g. selected="selected"
779
+ tagLine += "=\"#{tokens[count-1]}\""
780
+ expectedEqualityOP = false
781
+ next
782
+ end
783
+ count += 1
784
+ end
785
+ tagLine += "=\"#{tokens[count-1]}\" " if expectedEqualityOP == true
786
+ #puts tagLine
787
+ return tagLine
788
+ end
789
+ private :all_tag_attributes
790
+
791
+ # This function is used to escape the characters that are not valid XML data.
792
+ def xml_escape(str)
793
+ str = str.gsub(/&/,'&amp;')
794
+ str = str.gsub(/</,'&lt;')
795
+ str = str.gsub(/>/,'&gt;')
796
+ str = str.gsub(/"/, '&quot;')
797
+ str
798
+ end
799
+ private :xml_escape
800
+
801
+ # Returns HTML Source
802
+ # Traverse the DOM tree rooted at body element
803
+ # and generate the HTML source.
804
+ # element: Represent Current element
805
+ # htmlString:HTML Source
806
+ # spaces:(Used for debugging). Helps in indentation
807
+ def html_source(element, htmlString, spaceString)
808
+ begin
809
+ tagLine = ""
810
+ outerHtml = ""
811
+ tagName = ""
812
+ begin
813
+ tagName = element.tagName.downcase
814
+ tagName = EMPTY_TAG_NAME if tagName == ""
815
+ # If tag is a mismatched tag.
816
+ if !(tagName =~ /^(\w|_|:)(.*)$/)
817
+ return htmlString
818
+ end
819
+ rescue
820
+ #handling text nodes
821
+ htmlString += xml_escape(element.toString)
822
+ return htmlString
823
+ end
824
+ #puts tagName
825
+ #Skip comment and script tag
826
+ if tagName =~ /^!/ || tagName== "script" || tagName =="style"
827
+ return htmlString
828
+ end
829
+ #tagLine += spaceString
830
+ outerHtml = all_tag_attributes(element.outerHtml) if tagName != EMPTY_TAG_NAME
831
+ tagLine += "<#{tagName} #{outerHtml}"
832
+
833
+ canHaveChildren = element.canHaveChildren
834
+ if canHaveChildren
835
+ tagLine += ">"
836
+ else
837
+ tagLine += "/>" #self closing tag
838
+ end
839
+ #spaceString += spaceString
840
+ htmlString += tagLine
841
+ childElements = element.childnodes
842
+ childElements.each do |child|
843
+ htmlString = html_source(child,htmlString,spaceString)
844
+ end
845
+ if canHaveChildren
846
+ #tagLine += spaceString
847
+ tagLine ="</" + tagName + ">"
848
+ htmlString += tagLine
849
+ end
850
+ return htmlString
851
+ rescue => e
852
+ puts e.to_s
853
+ end
854
+ return htmlString
855
+ end
856
+ private :html_source
857
+
858
+ # return the first element that matches the xpath
859
+ def element_by_xpath(xpath)
860
+ temp = elements_by_xpath(xpath)
861
+ temp = temp[0] if temp
862
+ return temp
863
+ end
864
+
865
+ # execute xpath and return an array of elements
866
+ def elements_by_xpath(xpath)
867
+ doc = rexml_document_object
868
+ modifiedXpath = ""
869
+ selectedElements = Array.new
870
+ doc.elements.each(xpath) do |element|
871
+ modifiedXpath = element.xpath # element = a REXML element
872
+ # puts "modified xpath: #{modifiedXpath}"
873
+ # puts "text: #{element.text}"
874
+ # puts "class: #{element.attributes['class']}"
875
+ # require 'breakpoint'; breakpoint
876
+ temp = element_by_absolute_xpath(modifiedXpath) # temp = a DOM/COM element
877
+ selectedElements << temp if temp != nil
878
+ end
879
+ #puts selectedElements.length
880
+ if selectedElements.length == 0
881
+ return nil
882
+ else
883
+ return selectedElements
884
+ end
885
+ end
886
+
887
+ # Method that iterates over IE DOM object and get the elements for the given
888
+ # xpath.
889
+ def element_by_absolute_xpath(xpath)
890
+ curElem = nil
891
+
892
+ #puts "Hello; Given xpath is : #{xpath}"
893
+ doc = document
894
+ curElem = doc.getElementsByTagName("body")["0"]
895
+ xpath =~ /^.*\/body\[?\d*\]?\/(.*)/
896
+ xpath = $1
897
+
898
+ if xpath == nil
899
+ puts "Function Requires absolute XPath."
900
+ return
901
+ end
902
+
903
+ arr = xpath.split(/\//)
904
+ return nil if arr.length == 0
905
+
906
+ lastTagName = arr[arr.length-1].to_s.upcase
907
+
908
+ # lastTagName is like tagName[number] or just tagName. For the first case we need to
909
+ # separate tagName and number.
910
+ lastTagName =~ /(\w*)\[?\d*\]?/
911
+ lastTagName = $1
912
+ #puts lastTagName
913
+
914
+ for element in arr do
915
+ element =~ /(\w*)\[?(\d*)\]?/
916
+ tagname = $1
917
+ tagname = tagname.upcase
918
+
919
+ if $2 != nil && $2 != ""
920
+ index = $2
921
+ index = "#{index}".to_i - 1
922
+ else
923
+ index = 0
924
+ end
925
+
926
+ #puts "#{element} #{tagname} #{index}"
927
+ allElemns = curElem.childnodes
928
+ if allElemns == nil || allElemns.length == 0
929
+ puts "#{element} is null"
930
+ next # Go to next element
931
+ end
932
+
933
+ #puts "Current element is : #{curElem.tagName}"
934
+ allElemns.each do |child|
935
+ gotIt = false
936
+ begin
937
+ curTag = child.tagName
938
+ curTag = EMPTY_TAG_NAME if curTag == ""
939
+ rescue
940
+ next
941
+ end
942
+ #puts child.tagName
943
+ if curTag == tagname
944
+ index-=1
945
+ if index < 0
946
+ curElem = child
947
+ break
948
+ end
949
+ end
950
+ end
951
+
952
+ #puts "Node selected at index #{index.to_s} : #{curElem.tagName}"
953
+ end
954
+ begin
955
+ if curElem.tagName == lastTagName
956
+ #puts curElem.tagName
957
+ return curElem
958
+ else
959
+ return nil
960
+ end
961
+ rescue
962
+ return nil
963
+ end
964
+ end
965
+ private :element_by_absolute_xpath
966
+
967
+ def attach_command
968
+ "Watir::IE.attach(:hwnd, #{hwnd})"
969
+ end
970
+
971
+
972
+ end # class IE
973
+ end