watir-classic 3.0.0

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.
Files changed (213) hide show
  1. data/CHANGES +721 -0
  2. data/LICENSE +34 -0
  3. data/README.rdoc +78 -0
  4. data/VERSION +1 -0
  5. data/bin/watir-console +5 -0
  6. data/lib/watir-classic.rb +14 -0
  7. data/lib/watir-classic/IEDialog/Release/IEDialog.dll +0 -0
  8. data/lib/watir-classic/assertions.rb +44 -0
  9. data/lib/watir-classic/browser.rb +149 -0
  10. data/lib/watir-classic/browsers.rb +7 -0
  11. data/lib/watir-classic/close_all.rb +31 -0
  12. data/lib/watir-classic/container.rb +110 -0
  13. data/lib/watir-classic/contrib/enabled_popup.rb +21 -0
  14. data/lib/watir-classic/contrib/ie-new-process.rb +27 -0
  15. data/lib/watir-classic/contrib/page_checker.rb +29 -0
  16. data/lib/watir-classic/cookies.rb +80 -0
  17. data/lib/watir-classic/core.rb +43 -0
  18. data/lib/watir-classic/dialogs/file_field.rb +34 -0
  19. data/lib/watir-classic/dialogs/javascript.rb +43 -0
  20. data/lib/watir-classic/drag_and_drop_helper.rb +68 -0
  21. data/lib/watir-classic/element.rb +438 -0
  22. data/lib/watir-classic/element_collection.rb +109 -0
  23. data/lib/watir-classic/element_extensions.rb +69 -0
  24. data/lib/watir-classic/exceptions.rb +50 -0
  25. data/lib/watir-classic/form.rb +96 -0
  26. data/lib/watir-classic/frame.rb +47 -0
  27. data/lib/watir-classic/ie-class.rb +767 -0
  28. data/lib/watir-classic/ie-process.rb +47 -0
  29. data/lib/watir-classic/ie.rb +20 -0
  30. data/lib/watir-classic/image.rb +111 -0
  31. data/lib/watir-classic/input_elements.rb +480 -0
  32. data/lib/watir-classic/irb-history.rb +31 -0
  33. data/lib/watir-classic/link.rb +46 -0
  34. data/lib/watir-classic/locator.rb +243 -0
  35. data/lib/watir-classic/logger.rb +19 -0
  36. data/lib/watir-classic/matches.rb +23 -0
  37. data/lib/watir-classic/modal_dialog.rb +72 -0
  38. data/lib/watir-classic/module.rb +12 -0
  39. data/lib/watir-classic/non_control_elements.rb +114 -0
  40. data/lib/watir-classic/options.rb +56 -0
  41. data/lib/watir-classic/page-container.rb +114 -0
  42. data/lib/watir-classic/process.rb +20 -0
  43. data/lib/watir-classic/screen_capture.rb +115 -0
  44. data/lib/watir-classic/supported_elements.rb +172 -0
  45. data/lib/watir-classic/table.rb +224 -0
  46. data/lib/watir-classic/testcase.rb +97 -0
  47. data/lib/watir-classic/util.rb +35 -0
  48. data/lib/watir-classic/version.rb +4 -0
  49. data/lib/watir-classic/wait.rb +41 -0
  50. data/lib/watir-classic/wait_helper.rb +12 -0
  51. data/lib/watir-classic/waiter.rb +98 -0
  52. data/lib/watir-classic/win32.rb +40 -0
  53. data/lib/watir-classic/win32ole.rb +16 -0
  54. data/lib/watir-classic/win32ole/1.8.7/win32ole.so +0 -0
  55. data/lib/watir-classic/win32ole/1.9.3/win32ole.so +0 -0
  56. data/lib/watir-classic/window.rb +68 -0
  57. data/lib/watir-classic/xpath_locator.rb +52 -0
  58. data/rakefile.rb +54 -0
  59. data/unittests/all_tests.rb +10 -0
  60. data/unittests/buttons_xpath_test.rb +68 -0
  61. data/unittests/checkbox_test.rb +163 -0
  62. data/unittests/checkbox_xpath_test.rb +106 -0
  63. data/unittests/click_no_wait_test.rb +23 -0
  64. data/unittests/close_all_test.rb +17 -0
  65. data/unittests/core_tests.rb +17 -0
  66. data/unittests/css_selector_test.rb +44 -0
  67. data/unittests/css_test.rb +38 -0
  68. data/unittests/dialog_test.rb +64 -0
  69. data/unittests/div2_xpath_test.rb +21 -0
  70. data/unittests/div_test.rb +170 -0
  71. data/unittests/div_xpath_test.rb +95 -0
  72. data/unittests/document_standards.rb +63 -0
  73. data/unittests/element_collection_indexes_test.rb +57 -0
  74. data/unittests/element_collections_test.rb +100 -0
  75. data/unittests/element_test.rb +47 -0
  76. data/unittests/errorchecker_test.rb +31 -0
  77. data/unittests/filefield_test.rb +43 -0
  78. data/unittests/filefield_xpath_test.rb +35 -0
  79. data/unittests/form_test.rb +282 -0
  80. data/unittests/form_xpath_test.rb +254 -0
  81. data/unittests/frame_test.rb +165 -0
  82. data/unittests/google_form_test.rb +15 -0
  83. data/unittests/html/JavascriptClick.html +39 -0
  84. data/unittests/html/blankpage.html +11 -0
  85. data/unittests/html/buttons1.html +40 -0
  86. data/unittests/html/checkboxes1.html +89 -0
  87. data/unittests/html/click_no_wait.html +14 -0
  88. data/unittests/html/complex_table.html +35 -0
  89. data/unittests/html/cssTest.html +42 -0
  90. data/unittests/html/depot_store.html +59 -0
  91. data/unittests/html/div.html +92 -0
  92. data/unittests/html/div_xml.html +21 -0
  93. data/unittests/html/fileupload.html +44 -0
  94. data/unittests/html/formTest1.html +38 -0
  95. data/unittests/html/forms2.html +44 -0
  96. data/unittests/html/forms3.html +131 -0
  97. data/unittests/html/forms4.html +26 -0
  98. data/unittests/html/frame_buttons.html +4 -0
  99. data/unittests/html/frame_links.html +4 -0
  100. data/unittests/html/frame_multi.html +5 -0
  101. data/unittests/html/google_india.html +119 -0
  102. data/unittests/html/ie7_document_standards.html +9 -0
  103. data/unittests/html/ie8_document_standards.html +9 -0
  104. data/unittests/html/ie9_document_standards.html +9 -0
  105. data/unittests/html/iframe.html +3 -0
  106. data/unittests/html/iframeTest.html +17 -0
  107. data/unittests/html/iframeTest1.html +7 -0
  108. data/unittests/html/iframeTest2.html +5 -0
  109. data/unittests/html/images/1.gif +0 -0
  110. data/unittests/html/images/2.GIF +0 -0
  111. data/unittests/html/images/3.GIF +0 -0
  112. data/unittests/html/images/button.jpg +0 -0
  113. data/unittests/html/images/circle.jpg +0 -0
  114. data/unittests/html/images/map.GIF +0 -0
  115. data/unittests/html/images/map2.gif +0 -0
  116. data/unittests/html/images/minus.GIF +0 -0
  117. data/unittests/html/images/originaltriangle.jpg +0 -0
  118. data/unittests/html/images/plus.gif +0 -0
  119. data/unittests/html/images/square.jpg +0 -0
  120. data/unittests/html/images/triangle.jpg +0 -0
  121. data/unittests/html/images1.html +65 -0
  122. data/unittests/html/javascriptevents.html +33 -0
  123. data/unittests/html/link_pass.html +11 -0
  124. data/unittests/html/links1.html +37 -0
  125. data/unittests/html/links2.html +11 -0
  126. data/unittests/html/links_multi.html +12 -0
  127. data/unittests/html/list_matters.html +720 -0
  128. data/unittests/html/lists.html +18 -0
  129. data/unittests/html/map_test.html +30 -0
  130. data/unittests/html/modal_dialog.html +10 -0
  131. data/unittests/html/modal_dialog_launcher.html +12 -0
  132. data/unittests/html/multiple_specifiers.html +64 -0
  133. data/unittests/html/nestedFrames.html +6 -0
  134. data/unittests/html/new_browser.html +17 -0
  135. data/unittests/html/pass.html +13 -0
  136. data/unittests/html/popups1.html +59 -0
  137. data/unittests/html/pre.html +29 -0
  138. data/unittests/html/quirks_document_standards.html +8 -0
  139. data/unittests/html/radioButtons1.html +71 -0
  140. data/unittests/html/select_tealeaf.html +54 -0
  141. data/unittests/html/selectboxes1.html +52 -0
  142. data/unittests/html/simple_table.html +25 -0
  143. data/unittests/html/simple_table_buttons.html +104 -0
  144. data/unittests/html/simple_table_columns.html +75 -0
  145. data/unittests/html/table1.html +179 -0
  146. data/unittests/html/tableCell_using_xpath.html +19 -0
  147. data/unittests/html/table_and_tablerow_to_a.html +174 -0
  148. data/unittests/html/textarea.html +30 -0
  149. data/unittests/html/textfields1.html +100 -0
  150. data/unittests/html/textsearch.html +44 -0
  151. data/unittests/html/wallofcheckboxes.html +1003 -0
  152. data/unittests/html/xpath_nbsp.html +11 -0
  153. data/unittests/html/zeroindex.html +11 -0
  154. data/unittests/ie_exists_test.rb +16 -0
  155. data/unittests/ie_mock.rb +94 -0
  156. data/unittests/ie_test.rb +54 -0
  157. data/unittests/images_test.rb +156 -0
  158. data/unittests/images_xpath_test.rb +90 -0
  159. data/unittests/index_specifier_test.rb +31 -0
  160. data/unittests/js_events_test.rb +31 -0
  161. data/unittests/links_multi_test.rb +34 -0
  162. data/unittests/links_test.rb +131 -0
  163. data/unittests/links_xpath_test.rb +38 -0
  164. data/unittests/lists_test.rb +23 -0
  165. data/unittests/map_test.rb +98 -0
  166. data/unittests/minmax_test.rb +37 -0
  167. data/unittests/navigate_test.rb +38 -0
  168. data/unittests/nbsp_xpath_test.rb +16 -0
  169. data/unittests/no_wait_test.rb +28 -0
  170. data/unittests/non_core_tests.rb +12 -0
  171. data/unittests/other/all_tests_concurrent.rb +57 -0
  172. data/unittests/other/navigate_exception_test.rb +24 -0
  173. data/unittests/other/rexml_unit_test.rb +27 -0
  174. data/unittests/other/screen_capture_test.rb +53 -0
  175. data/unittests/other/testcase_method_order_test.rb +36 -0
  176. data/unittests/other/testcase_verify_test.rb +25 -0
  177. data/unittests/other/wait_until_test.rb +102 -0
  178. data/unittests/pagecontainstext_test.rb +69 -0
  179. data/unittests/parent_child_test.rb +27 -0
  180. data/unittests/perf_test.rb +20 -0
  181. data/unittests/pre_test.rb +49 -0
  182. data/unittests/radios_test.rb +181 -0
  183. data/unittests/radios_xpath_test.rb +100 -0
  184. data/unittests/security_setting_test.rb +24 -0
  185. data/unittests/selectbox_test.rb +144 -0
  186. data/unittests/selectbox_xpath_test.rb +102 -0
  187. data/unittests/setup.rb +69 -0
  188. data/unittests/speed_settings_test.rb +67 -0
  189. data/unittests/table_cell_using_xpath_test.rb +34 -0
  190. data/unittests/table_test.rb +296 -0
  191. data/unittests/table_xpath_test.rb +109 -0
  192. data/unittests/test_tests.rb +9 -0
  193. data/unittests/textarea_test.rb +92 -0
  194. data/unittests/textarea_xpath_test.rb +77 -0
  195. data/unittests/textfield_for_ch_char_test.rb +32 -0
  196. data/unittests/textfields_test.rb +184 -0
  197. data/unittests/textfields_xpath_test.rb +110 -0
  198. data/unittests/version_test.rb +15 -0
  199. data/unittests/win32ole_so_test.rb +35 -0
  200. data/unittests/window_tests.rb +10 -0
  201. data/unittests/windows/attach_to_existing_window_test.rb +52 -0
  202. data/unittests/windows/attach_to_new_window_test.rb +74 -0
  203. data/unittests/windows/close_window_test.rb +20 -0
  204. data/unittests/windows/frame_links_test.rb +23 -0
  205. data/unittests/windows/ie-each_test.rb +46 -0
  206. data/unittests/windows/modal_dialog_test.rb +95 -0
  207. data/unittests/windows/new_process_test.rb +24 -0
  208. data/unittests/windows/new_test.rb +58 -0
  209. data/unittests/windows/open_close_test.rb +19 -0
  210. data/unittests/windows/send_keys_test.rb +26 -0
  211. data/unittests/xpath_tests.rb +11 -0
  212. data/watir-rdoc.rb +7 -0
  213. metadata +370 -0
@@ -0,0 +1,109 @@
1
+ module Watir
2
+ # this class is the super class for the iterator classes (buttons, links, spans etc
3
+ # it would normally only be accessed by the iterator methods (spans, links etc) of IE
4
+ class ElementCollection
5
+ include Enumerable
6
+
7
+ # Super class for all the iterator classes
8
+ # * container - an instance of an IE object
9
+ def initialize(container, specifiers)
10
+ if specifiers[:index]
11
+ raise Exception::MissingWayOfFindingObjectException,
12
+ "#{self.class} does not support attribute :index in #{specifiers.inspect}"
13
+ end
14
+
15
+ @container = container
16
+ @specifiers = specifiers
17
+ @page_container = container.page_container
18
+ end
19
+
20
+ def length
21
+ count = 0
22
+ each {|element| count += 1 }
23
+ count
24
+ end
25
+
26
+ alias_method :size, :length
27
+
28
+ # iterate through each of the elements in the collection in turn
29
+ def each
30
+ @container.locator_for(TaggedElementLocator, @specifiers, element_class).each {|element| yield element}
31
+ end
32
+
33
+ # allows access to a specific item in the collection
34
+ def [](n)
35
+ number = n - Watir::IE.base_index
36
+ offset = Watir::IE.zero_based_indexing ? (length - 1) : length
37
+ non_existing_element = element_class.new(@container, @specifiers.merge(:index => n))
38
+ def non_existing_element.locate; nil end
39
+ iterator_object(number) || non_existing_element
40
+ end
41
+
42
+ def first
43
+ iterator_object(0)
44
+ end
45
+
46
+ def last
47
+ iterator_object(length - 1)
48
+ end
49
+
50
+ def to_s
51
+ map { |e| e.to_s }.join("\n")
52
+ end
53
+
54
+ def inspect
55
+ '#<%s:0x%x length=%s container=%s>' % [self.class, hash*2, length.inspect, @container.inspect]
56
+ end
57
+
58
+ private
59
+
60
+ def iterator_object(i)
61
+ count = 0
62
+ each do |e|
63
+ return e if (i >= 0 && count == i) || (i < 0 && count == length + i)
64
+ count += 1
65
+ end
66
+ end
67
+
68
+ def element_class
69
+ Watir.const_get self.class.name.split("::").last.scan(/(.*)Collection/).flatten.first
70
+ end
71
+
72
+ end
73
+
74
+ class TableElementCollection < ElementCollection
75
+ def initialize(container, specifiers, ole_collection=nil)
76
+ super container, specifiers
77
+ @ole_collection = ole_collection
78
+ end
79
+
80
+ def each
81
+ if @ole_collection
82
+ elements = []
83
+ @ole_collection.each {|element| elements << element_class.new(@container, :ole_object => element)}
84
+ super do |element|
85
+ yield element if elements.include?(element)
86
+ end
87
+ else
88
+ super
89
+ end
90
+ end
91
+ end
92
+
93
+ class TableRowCollection < TableElementCollection; end
94
+
95
+ class TableCellCollection < TableElementCollection; end
96
+
97
+ class InputElementCollection < ElementCollection
98
+ def each
99
+ @container.locator_for(InputElementLocator, @specifiers, element_class).each {|element| yield element}
100
+ end
101
+ end
102
+
103
+ class HTMLElementCollection < ElementCollection
104
+ def each
105
+ @container.locator_for(TaggedElementLocator, @specifiers, Element).each { |element| yield element }
106
+ end
107
+ end
108
+
109
+ end
@@ -0,0 +1,69 @@
1
+ # encoding: utf-8
2
+
3
+ module Watir
4
+ # This assumes that Element#visible? is defined
5
+ module ElementExtensions
6
+
7
+ #
8
+ # Wraps a {Celerity,Watir}::Element so that any subsequent method calls are
9
+ # put on hold until the element is present on the page.
10
+ #
11
+
12
+ class WhenPresentDecorator
13
+ def initialize(element, timeout)
14
+ @element = element
15
+ @timeout = timeout
16
+ end
17
+
18
+ def method_missing(m, *args, &block)
19
+ Watir::Wait.until(@timeout) { @element.present? }
20
+ @element.send(m, *args, &block)
21
+ end
22
+
23
+ # Returns element id
24
+ def id
25
+ Watir::Wait.until(@timeout) { @element.present? }
26
+ @element.id
27
+ end
28
+
29
+ end
30
+
31
+ #
32
+ # Returns true if the element exists and is visible on the page
33
+ #
34
+
35
+ def present?
36
+ exists? && visible? rescue false
37
+ end
38
+
39
+ #
40
+ # Waits until the element is present.
41
+ #
42
+ # Optional argument:
43
+ #
44
+ # timeout - seconds to wait before timing out (default: 60)
45
+ #
46
+ # browser.button(:id, 'foo').when_present.click
47
+ # browser.div(:id, 'bar').when_present { |div| ... }
48
+ # browser.p(:id, 'baz').when_present(60).text
49
+ #
50
+
51
+ def when_present(timeout = 60)
52
+ if block_given?
53
+ Watir::Wait.until(timeout) { self.present? }
54
+ yield self
55
+ else
56
+ return WhenPresentDecorator.new(self, timeout)
57
+ end
58
+ end
59
+
60
+ def wait_until_present(timeout = 60)
61
+ Watir::Wait.until(timeout) { self.present? }
62
+ end
63
+
64
+ def wait_while_present(timeout = 60)
65
+ Watir::Wait.while(timeout) { self.present? }
66
+ end
67
+
68
+ end # module ElementExtensions
69
+ end
@@ -0,0 +1,50 @@
1
+ module Watir
2
+ module Exception
3
+
4
+ # Root class for all Watir Exceptions
5
+ class WatirException < RuntimeError
6
+ def initialize(message="")
7
+ super(message)
8
+ end
9
+ end
10
+
11
+ # This exception is raised if an attempt is made to access an object that doesn't exist
12
+ class UnknownObjectException < WatirException; end
13
+ # This exception is raised if an attempt is made to access an object that is in a disabled state
14
+ class ObjectDisabledException < WatirException; end
15
+ # This exception is raised if an attempt is made to access a frame that cannot be found
16
+ class UnknownFrameException < WatirException; end
17
+ # This exception is raised if an attempt is made to access a frame that IE is denying access to
18
+ class FrameAccessDeniedException < WatirException; end
19
+ # This exception is raised if an attempt is made to access a form that cannot be found
20
+ class UnknownFormException< WatirException; end
21
+ # This exception is raised if an attempt is made to access an object that is in a read only state
22
+ class ObjectReadOnlyException < WatirException; end
23
+ # This exception is raised if an attempt is made to access an object when the specified value cannot be found
24
+ class NoValueFoundException < WatirException; end
25
+ # This exception gets raised if part of finding an object is missing
26
+ class MissingWayOfFindingObjectException < WatirException; end
27
+ # this exception is raised if an attempt is made to access a table cell that doesnt exist
28
+ class UnknownCellException < WatirException; end
29
+ # This exception is raised if the window cannot be found
30
+ class NoMatchingWindowFoundException < WatirException; end
31
+ # This exception is raised if an attemp is made to acces the status bar of the browser when it doesnt exist
32
+ class NoStatusBarException < WatirException; end
33
+ # This exception is raised if an http error, such as a 404, 500 etc is encountered while navigating
34
+ class NavigationException < WatirException; end
35
+ # This exception is raised when an event is fired that we don't know how to handle
36
+ class UnhandledEventException < WatirException; end
37
+ # This exception is raised if a timeout is exceeded
38
+ class TimeOutException < WatirException
39
+ def initialize(duration, timeout)
40
+ @duration, @timeout = duration, timeout
41
+ end
42
+ attr_reader :duration, :timeout
43
+ end
44
+
45
+ # Return an error message for when unable to locate the element
46
+ def self.message_for_unable_to_locate(specifiers)
47
+ "Unable to locate element, using #{specifiers.inspect}"
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,96 @@
1
+ module Watir
2
+
3
+ class Form < Element
4
+ def initialize(container, specifiers)
5
+ super
6
+ copy_test_config container
7
+ end
8
+
9
+ attr_ole :action
10
+
11
+ def name
12
+ assert_exists
13
+ name = ole_object.getAttributeNode('name')
14
+ name ? name.value : ''
15
+ end
16
+
17
+ def form_method
18
+ assert_exists
19
+ ole_object.invoke('method')
20
+ end
21
+
22
+ def method(arg = nil)
23
+ if arg.nil?
24
+ form_method
25
+ else
26
+ super
27
+ end
28
+ end
29
+
30
+ def locate
31
+ @o = @container.locator_for(FormLocator, @specifiers, self.class).locate
32
+ end
33
+
34
+ # Submit the data -- equivalent to pressing Enter or Return to submit a form.
35
+ def submit
36
+ assert_exists
37
+ @o.submit(0) if dispatch_event "onSubmit"
38
+ @container.wait
39
+ end
40
+
41
+ def __ole_inner_elements
42
+ assert_exists
43
+ @o.elements
44
+ end
45
+
46
+ # This method is responsible for setting and clearing the colored highlighting on the specified form.
47
+ # use :set to set the highlight
48
+ # :clear to clear the highlight
49
+ def highlight(set_or_clear, element, count)
50
+ if set_or_clear == :set
51
+ begin
52
+ original_color = element.style.backgroundColor
53
+ original_color = "" if original_color==nil
54
+ element.style.backgroundColor = activeObjectHighLightColor
55
+ rescue => e
56
+ puts e
57
+ puts e.backtrace.join("\n")
58
+ original_color = ""
59
+ end
60
+ @original_styles[count] = original_color
61
+ else
62
+ begin
63
+ element.style.backgroundColor = @original_styles[ count]
64
+ rescue => e
65
+ puts e
66
+ # we could be here for a number of reasons...
67
+ ensure
68
+ end
69
+ end
70
+ end
71
+ private :highlight
72
+
73
+ # causes the object to flash. Normally used in IRB when creating scripts
74
+ # Default is 10
75
+ def flash number=10
76
+ assert_exists
77
+ @original_styles = {}
78
+ number.times do
79
+ count = 0
80
+ @o.elements.each do |element|
81
+ highlight(:set, element, count)
82
+ count += 1
83
+ end
84
+ sleep 0.05
85
+ count = 0
86
+ @o.elements.each do |element|
87
+ highlight(:clear, element, count)
88
+ count += 1
89
+ end
90
+ sleep 0.05
91
+ end
92
+ end
93
+
94
+ end # class Form
95
+
96
+ end
@@ -0,0 +1,47 @@
1
+ module Watir
2
+ class Frame < Element
3
+ include PageContainer
4
+ attr_accessor :document
5
+
6
+ attr_ole :name
7
+ attr_ole :src
8
+
9
+ def initialize(container, specifiers)
10
+ super
11
+ copy_test_config container
12
+ end
13
+
14
+ # Find the frame denoted by specifiers in the container and return its ole_object
15
+ def locate
16
+ frame, document = @container.locator_for(FrameLocator, @specifiers, self.class).locate
17
+ if frame && document
18
+ @o = frame
19
+ begin
20
+ @document = document.document
21
+ rescue WIN32OLERuntimeError => e
22
+ # This frame's content is not directly accessible but let the
23
+ # user continue so they can access the frame properties
24
+ raise e unless e.message =~ /Access is denied/
25
+ end
26
+ end
27
+ end
28
+
29
+ def __ole_inner_elements
30
+ document.body.all
31
+ end
32
+
33
+ def document
34
+ assert_exists
35
+ if @document
36
+ @document
37
+ else
38
+ raise FrameAccessDeniedException, "IE will not allow access to this frame for security reasons. You can work around this with ie.goto(frame.src)"
39
+ end
40
+ end
41
+
42
+ def attach_command
43
+ @container.page_container.attach_command + ".frame(#{@specifiers.inspect})".gsub('"','\'')
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,767 @@
1
+ module Watir
2
+ class IE
3
+ include WaitHelper
4
+ include Exception
5
+ include Container
6
+ include PageContainer
7
+
8
+ # Maximum number of seconds to wait when attaching to a window
9
+ @@attach_timeout = 2.0 # default value
10
+ def self.attach_timeout
11
+ @@attach_timeout
12
+ end
13
+ def self.attach_timeout=(timeout)
14
+ @@attach_timeout = timeout
15
+ end
16
+
17
+ # Return the options used when creating new instances of IE.
18
+ # BUG: this interface invites misunderstanding/misuse such as IE.options[:speed] = :zippy]
19
+ def self.options
20
+ {:speed => self.speed, :visible => self.visible, :attach_timeout => self.attach_timeout, :zero_based_indexing => self.zero_based_indexing}
21
+ end
22
+ # set values for options used when creating new instances of IE.
23
+ def self.set_options options
24
+ options.each do |name, value|
25
+ send "#{name}=", value
26
+ end
27
+ end
28
+ # The globals $FAST_SPEED and $HIDE_IE are checked both at initialization
29
+ # and later, because they
30
+ # might be set after initialization. Setting them beforehand (e.g. from
31
+ # the command line) will affect the class, otherwise it is only a temporary
32
+ # effect
33
+ @@speed = $FAST_SPEED ? :fast : :slow
34
+ def self.speed
35
+ return :fast if $FAST_SPEED
36
+ @@speed
37
+ end
38
+ def self.speed= x
39
+ $FAST_SPEED = nil
40
+ @@speed = x
41
+ end
42
+ @@visible = $HIDE_IE ? false : true
43
+ def self.visible
44
+ return false if $HIDE_IE
45
+ @@visible
46
+ end
47
+ def self.visible= x
48
+ $HIDE_IE = nil
49
+ @@visible = x
50
+ end
51
+
52
+ @@zero_based_indexing = true
53
+ def self.zero_based_indexing= enabled
54
+ @@zero_based_indexing = enabled
55
+ end
56
+
57
+ def self.zero_based_indexing
58
+ @@zero_based_indexing
59
+ end
60
+
61
+ def self.base_index
62
+ self.zero_based_indexing ? 0 : 1
63
+ end
64
+
65
+ # Used internally to determine when IE has finished loading a page
66
+ READYSTATES = {:complete => 4}
67
+
68
+ # The default color for highlighting objects as they are accessed.
69
+ HIGHLIGHT_COLOR = 'yellow'
70
+
71
+ # The time, in seconds, it took for the new page to load after executing the
72
+ # the last command
73
+ attr_reader :down_load_time
74
+
75
+ # the OLE Internet Explorer object
76
+ attr_accessor :ie
77
+ # access to the logger object
78
+ attr_accessor :logger
79
+
80
+ # this contains the list of unique urls that have been visited
81
+ attr_reader :url_list
82
+
83
+ # Create a new IE window. Works just like IE.new in Watir 1.4.
84
+ def self.new_window
85
+ ie = new true
86
+ ie._new_window_init
87
+ ie
88
+ end
89
+
90
+ # Create an IE browser.
91
+ def initialize suppress_new_window=nil
92
+ _new_window_init unless suppress_new_window
93
+ end
94
+
95
+ def _new_window_init
96
+ create_browser_window
97
+ initialize_options
98
+ goto 'about:blank' # this avoids numerous problems caused by lack of a document
99
+ end
100
+
101
+ # Create a new IE Window, starting at the specified url.
102
+ # If no url is given, start empty.
103
+ def self.start url=nil
104
+ start_window url
105
+ end
106
+
107
+ # Create a new IE window, starting at the specified url.
108
+ # If no url is given, start empty. Works like IE.start in Watir 1.4.
109
+ def self.start_window url=nil
110
+ ie = new_window
111
+ ie.goto url if url
112
+ ie
113
+ end
114
+
115
+ # Create a new IE window in a new process.
116
+ # This method will not work when
117
+ # Watir/Ruby is run under a service (instead of a user).
118
+ def self.new_process
119
+ ie = new true
120
+ ie._new_process_init
121
+ ie
122
+ end
123
+
124
+ def _new_process_init
125
+ iep = Process.start
126
+ @ie = iep.window
127
+ @process_id = iep.process_id
128
+ initialize_options
129
+ goto 'about:blank'
130
+ end
131
+
132
+ # Create a new IE window in a new process, starting at the specified URL.
133
+ # Same as IE.start.
134
+ def self.start_process url=nil
135
+ ie = new_process
136
+ ie.goto url if url
137
+ ie
138
+ end
139
+
140
+ # Return a Watir::IE object for an existing IE window. Window can be
141
+ # referenced by url, title, or window handle.
142
+ # Second argument can be either a string or a regular expression in the
143
+ # case of of :url or :title.
144
+ # IE.attach(:url, 'http://www.google.com')
145
+ # IE.attach(:title, 'Google')
146
+ # IE.attach(:hwnd, 528140)
147
+ # This method will not work when
148
+ # Watir/Ruby is run under a service (instead of a user).
149
+ def self.attach how, what
150
+ ie = new true # don't create window
151
+ ie._attach_init(how, what)
152
+ ie
153
+ end
154
+
155
+ # this method is used internally to attach to an existing window
156
+ def _attach_init how, what
157
+ attach_browser_window how, what
158
+ initialize_options
159
+ wait
160
+ end
161
+
162
+ # Return an IE object that wraps the given window, typically obtained from
163
+ # Shell.Application.windows.
164
+ def self.bind window
165
+ ie = new true
166
+ ie.ie = window
167
+ ie.initialize_options
168
+ ie
169
+ end
170
+
171
+ def initialize_options
172
+ self.visible = IE.visible
173
+ self.speed = IE.speed
174
+
175
+ @ole_object = nil
176
+ @page_container = self
177
+ @error_checkers = []
178
+ @activeObjectHighLightColor = HIGHLIGHT_COLOR
179
+
180
+
181
+ @logger = DefaultLogger.new
182
+ @url_list = []
183
+ end
184
+
185
+ # Specifies the speed that commands will be executed at. Choices are:
186
+ # * :slow (default)
187
+ # * :fast
188
+ # * :zippy
189
+ # With IE#speed= :zippy, text fields will be entered at once, instead of
190
+ # character by character (default).
191
+ def speed= how_fast
192
+ case how_fast
193
+ when :zippy then
194
+ @typingspeed = 0
195
+ @pause_after_wait = 0.01
196
+ @type_keys = false
197
+ @speed = :fast
198
+ when :fast then
199
+ @typingspeed = 0
200
+ @pause_after_wait = 0.01
201
+ @type_keys = true
202
+ @speed = :fast
203
+ when :slow then
204
+ @typingspeed = 0.08
205
+ @pause_after_wait = 0.1
206
+ @type_keys = true
207
+ @speed = :slow
208
+ else
209
+ raise ArgumentError, "Invalid speed: #{how_fast}"
210
+ end
211
+ end
212
+
213
+ def speed
214
+ return @speed if @speed == :slow
215
+ return @type_keys ? :fast : :zippy
216
+ end
217
+
218
+ # deprecated: use speed = :fast instead
219
+ def set_fast_speed
220
+ self.speed = :fast
221
+ end
222
+
223
+ # deprecated: use speed = :slow instead
224
+ def set_slow_speed
225
+ self.speed = :slow
226
+ end
227
+
228
+ def visible
229
+ @ie.visible
230
+ end
231
+ def visible=(boolean)
232
+ @ie.visible = boolean if boolean != @ie.visible
233
+ end
234
+
235
+ # Yields successively to each IE window on the current desktop. Takes a block.
236
+ # This method will not work when
237
+ # Watir/Ruby is run under a service (instead of a user).
238
+ # Yields to the window and its hwnd.
239
+ def self.each
240
+ shell = WIN32OLE.new('Shell.Application')
241
+ ie_browsers = []
242
+ shell.Windows.each do |window|
243
+ next unless (window.path =~ /Internet Explorer/ rescue false)
244
+ next unless (hwnd = window.hwnd rescue false)
245
+ ie = IE.bind(window)
246
+ ie.hwnd = hwnd
247
+ ie_browsers << ie
248
+ end
249
+ ie_browsers.each do |ie|
250
+ yield ie
251
+ end
252
+ end
253
+
254
+ def self.version
255
+ @ie_version ||= begin
256
+ require 'win32/registry'
257
+ ::Win32::Registry::HKEY_LOCAL_MACHINE.open("SOFTWARE\\Microsoft\\Internet Explorer") do |ie_key|
258
+ ie_key.read('Version').last
259
+ end
260
+ # OR: ::WIN32OLE.new("WScript.Shell").RegRead("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Internet Explorer\\Version")
261
+ end
262
+ end
263
+
264
+ def self.version_parts
265
+ version.split('.')
266
+ end
267
+
268
+ # return internet explorer instance as specified. if none is found,
269
+ # return nil.
270
+ # arguments:
271
+ # :url, url -- the URL of the IE browser window
272
+ # :title, title -- the title of the browser page
273
+ # :hwnd, hwnd -- the window handle of the browser window.
274
+ # This method will not work when
275
+ # Watir/Ruby is run under a service (instead of a user).
276
+ def self.find(how, what)
277
+ ie_ole = IE._find(how, what)
278
+ IE.bind ie_ole if ie_ole
279
+ end
280
+
281
+ def self._find(how, what)
282
+ self._find_all(how, what).first
283
+ end
284
+
285
+ def self._find_all(how, what)
286
+ ies = []
287
+ count = -1
288
+ IE.each do |ie|
289
+ window = ie.ie
290
+
291
+ case how
292
+ when :url
293
+ ies << window if (what.matches(window.locationURL))
294
+ when :title
295
+ # normal windows explorer shells do not have document
296
+ # note window.document will fail for "new" browsers
297
+ begin
298
+ title = window.locationname
299
+ title = window.document.title
300
+ rescue WIN32OLERuntimeError
301
+ end
302
+ ies << window if what.matches(title)
303
+ when :hwnd
304
+ begin
305
+ ies << window if what == window.HWND
306
+ rescue WIN32OLERuntimeError
307
+ end
308
+ when :index
309
+ count += 1
310
+ if count == what
311
+ ies << window
312
+ break
313
+ end
314
+ when nil
315
+ ies << window
316
+ else
317
+ raise ArgumentError
318
+ end
319
+ end
320
+
321
+ ies
322
+ end
323
+
324
+ # Return the current window handle
325
+ def hwnd
326
+ raise "Not attached to a browser" if @ie.nil?
327
+ @hwnd ||= @ie.hwnd
328
+ end
329
+ attr_writer :hwnd
330
+
331
+ # Are we attached to an open browser?
332
+ def exists?
333
+ begin
334
+ !!(@ie.name =~ /Internet Explorer/)
335
+ rescue WIN32OLERuntimeError, NoMethodError
336
+ false
337
+ end
338
+ end
339
+ alias :exist? :exists?
340
+
341
+ # deprecated: use logger= instead
342
+ def set_logger(logger)
343
+ @logger = logger
344
+ end
345
+
346
+ def log(what)
347
+ @logger.debug(what) if @logger
348
+ end
349
+
350
+ #
351
+ # Accessing data outside the document
352
+ #
353
+
354
+ # Return the title of the document
355
+ def title
356
+ @ie.document.title
357
+ end
358
+
359
+ # Return the status of the window, typically from the status bar at the bottom.
360
+ def status
361
+ @ie.statusText
362
+ rescue WIN32OLERuntimeError
363
+ ""
364
+ end
365
+
366
+ #
367
+ # Navigation
368
+ #
369
+
370
+ # Navigate to the specified URL.
371
+ # * url - string - the URL to navigate to
372
+ def goto(url)
373
+ url = "http://" + url unless url =~ %r{://} || url == "about:blank"
374
+ @ie.navigate(url)
375
+ wait
376
+ return @down_load_time
377
+ end
378
+
379
+ # Go to the previous page - the same as clicking the browsers back button
380
+ # an WIN32OLERuntimeError exception is raised if the browser cant go back
381
+ def back
382
+ @ie.GoBack
383
+ wait
384
+ end
385
+
386
+ # Go to the next page - the same as clicking the browsers forward button
387
+ # an WIN32OLERuntimeError exception is raised if the browser cant go forward
388
+ def forward
389
+ @ie.GoForward
390
+ wait
391
+ end
392
+
393
+ # Refresh the current page - the same as clicking the browsers refresh button
394
+ # an WIN32OLERuntimeError exception is raised if the browser cant refresh
395
+ def refresh
396
+ @ie.refresh2(3)
397
+ wait
398
+ end
399
+
400
+ def inspect
401
+ '#<%s:0x%x url=%s title=%s>' % [self.class, hash*2, url.inspect, title.inspect]
402
+ end
403
+
404
+ # clear the list of urls that we have visited
405
+ def clear_url_list
406
+ @url_list.clear
407
+ end
408
+
409
+ # Closes the Browser
410
+ def close
411
+ return unless exists?
412
+ @ie.stop
413
+ wait rescue nil
414
+ chwnd = @ie.hwnd.to_i
415
+ @ie.quit
416
+ t = ::Time.now
417
+ while exists?
418
+ # just in case to avoid possible endless loop if failing to close some
419
+ # window or tab
420
+ break if ::Time.now - t > 10
421
+ sleep 0.3
422
+ end
423
+ end
424
+
425
+ # Maximize the window (expands to fill the screen)
426
+ def maximize
427
+ rautomation.maximize
428
+ end
429
+
430
+ # Minimize the window (appears as icon on taskbar)
431
+ def minimize
432
+ rautomation.minimize
433
+ end
434
+
435
+ def minimized?
436
+ rautomation.minimized?
437
+ end
438
+
439
+ # Restore the window (after minimizing or maximizing)
440
+ def restore
441
+ rautomation.restore
442
+ end
443
+
444
+ # Make the window come to the front
445
+ def activate
446
+ rautomation.activate
447
+ end
448
+ alias :bring_to_front :activate
449
+
450
+ def active?
451
+ rautomation.active?
452
+ end
453
+ alias :front? :active?
454
+
455
+ def rautomation
456
+ @rautomation ||= ::RAutomation::Window.new(:hwnd => hwnd)
457
+ @rautomation
458
+ end
459
+
460
+ def autoit
461
+ Kernel.warn "Usage of Watir::IE#autoit method is DEPRECATED! Use Watir::IE#rautomation method instead. Refer to https://github.com/jarmo/RAutomation for updating your scripts."
462
+ @autoit ||= ::RAutomation::Window.new(:hwnd => hwnd, :adapter => :autoit)
463
+ @autoit
464
+ end
465
+
466
+ # Activates the window and sends keys to it.
467
+ #
468
+ # Example:
469
+ # browser.send_keys("Hello World{enter}")
470
+ #
471
+ # Refer to RAutomation::Adapter::WinFfi::KeystrokeConverter.convert_special_characters for
472
+ # special characters conversion.
473
+ # @see RAutomation::Window#send_keys
474
+ def send_keys(*keys)
475
+ rautomation.send_keys *keys
476
+ end
477
+
478
+ def dir
479
+ return File.expand_path(File.dirname(__FILE__))
480
+ end
481
+
482
+ #
483
+ # Document and Document Data
484
+ #
485
+
486
+ # Return the current document
487
+ def document
488
+ return @ie.document
489
+ end
490
+
491
+ # returns the current url, as displayed in the address bar of the browser
492
+ def url
493
+ return @ie.LocationURL
494
+ end
495
+
496
+ def window(specifiers={}, &blk)
497
+ win = Window.new(self, specifiers, &blk)
498
+ win.use &blk if blk
499
+ win
500
+ end
501
+
502
+ def windows(specifiers={}, &blk)
503
+ self.class._find_all(specifiers.keys.first, specifiers.values.first).map {|ie| Window.new(self, specifiers, IE.bind(ie), &blk)}
504
+ end
505
+
506
+ def cookies
507
+ Cookies.new(self)
508
+ end
509
+
510
+ #
511
+ # Synchronization
512
+ #
513
+
514
+ # Block execution until the page has loaded.
515
+ #
516
+ # Will raise Timeout::Error if page hasn't been loaded within 5 minutes.
517
+ # =nodoc
518
+ # Note: This code needs to be prepared for the ie object to be closed at
519
+ # any moment!
520
+ def wait(no_sleep=false)
521
+ @xml_parser_doc = nil
522
+ @down_load_time = 0.0
523
+ interval = 0.05
524
+ start_load_time = ::Time.now
525
+
526
+ Timeout::timeout(5*60) do
527
+ begin
528
+ while @ie.busy
529
+ sleep interval
530
+ end
531
+
532
+ until READYSTATES.has_value?(@ie.readyState)
533
+ sleep interval
534
+ end
535
+
536
+ until @ie.document
537
+ sleep interval
538
+ end
539
+
540
+ documents_to_wait_for = [@ie.document]
541
+ rescue WIN32OLERuntimeError # IE window must have been closed
542
+ @down_load_time = ::Time.now - start_load_time
543
+ return @down_load_time
544
+ end
545
+
546
+ while doc = documents_to_wait_for.shift
547
+ begin
548
+ until READYSTATES.has_key?(doc.readyState.to_sym)
549
+ sleep interval
550
+ end
551
+ @url_list << doc.location.href unless @url_list.include?(doc.location.href)
552
+ doc.frames.length.times do |n|
553
+ begin
554
+ documents_to_wait_for << doc.frames[n.to_s].document
555
+ rescue WIN32OLERuntimeError, NoMethodError
556
+ end
557
+ end
558
+ rescue WIN32OLERuntimeError
559
+ end
560
+ end
561
+ end
562
+
563
+ @down_load_time = ::Time.now - start_load_time
564
+ run_error_checks
565
+ sleep @pause_after_wait unless no_sleep
566
+ @down_load_time
567
+ end
568
+
569
+ # Error checkers
570
+
571
+ # this method runs the predefined error checks
572
+ def run_error_checks
573
+ @error_checkers.each { |e| e.call(self) }
574
+ end
575
+
576
+ # this method is used to add an error checker that gets executed on every page load
577
+ # * checker Proc Object, that contains the code to be run
578
+ def add_checker(checker)
579
+ @error_checkers << checker
580
+ end
581
+
582
+ # this allows a checker to be disabled
583
+ # * checker Proc Object, the checker that is to be disabled
584
+ def disable_checker(checker)
585
+ @error_checkers.delete(checker)
586
+ end
587
+
588
+ #
589
+ # Show me state
590
+ #
591
+
592
+ # Show all forms displays all the forms that are on a web page.
593
+ def show_forms
594
+ if all_forms = self.forms
595
+ count = all_forms.length
596
+ puts "There are #{count} forms"
597
+ all_forms.each do |form|
598
+ puts "Form name: #{form.name}"
599
+ puts " id: #{form.id}"
600
+ puts " method: #{form.method}"
601
+ puts " action: #{form.action}"
602
+ end
603
+ else
604
+ puts "No forms"
605
+ end
606
+ end
607
+
608
+ # this method shows all the images availble in the document
609
+ def show_images
610
+ doc = document
611
+ index = 1
612
+ doc.images.each do |l|
613
+ puts "image: name: #{l.name}"
614
+ puts " id: #{l.invoke("id")}"
615
+ puts " src: #{l.src}"
616
+ puts " index: #{index}"
617
+ index += 1
618
+ end
619
+ end
620
+
621
+ # this method shows all the links availble in the document
622
+ def show_links
623
+ props = ["name", "id", "href"]
624
+ print_sizes = [12, 12, 60]
625
+ doc = document
626
+ index = 0
627
+ text_size = 60
628
+ # draw the table header
629
+ s = "index".ljust(6)
630
+ props.each_with_index do |p, i|
631
+ s += p.ljust(print_sizes[i])
632
+ end
633
+ s += "text/src".ljust(text_size)
634
+ s += "\n"
635
+
636
+ # now get the details of the links
637
+ doc.links.each do |n|
638
+ index += 1
639
+ s = s + index.to_s.ljust(6)
640
+ props.each_with_index do |prop, i|
641
+ printsize = print_sizes[i]
642
+ begin
643
+ p = n.invoke(prop)
644
+ temp_var = "#{p}".to_s.ljust(printsize)
645
+ rescue
646
+ # this object probably doesnt have this property
647
+ temp_var = "".to_s.ljust(printsize)
648
+ end
649
+ s += temp_var
650
+ end
651
+ s += n.innerText
652
+ if n.getElementsByTagName("IMG").length > 0
653
+ s += " / " + n.getElementsByTagName("IMG").item(0).src
654
+ end
655
+ s += "\n"
656
+ end
657
+ puts s
658
+ end
659
+
660
+ # this method shows the name, id etc of the object that is currently active - ie the element that has focus
661
+ # its mostly used in irb when creating a script
662
+ def show_active
663
+ s = ""
664
+
665
+ current = document.activeElement
666
+ begin
667
+ s += current.invoke("type").to_s.ljust(16)
668
+ rescue
669
+ end
670
+ props = ["name", "id", "value", "alt", "src", "innerText", "href"]
671
+ props.each do |prop|
672
+ begin
673
+ p = current.invoke(prop)
674
+ s += " " + "#{prop}=#{p}".to_s.ljust(18)
675
+ rescue
676
+ #this object probably doesnt have this property
677
+ end
678
+ end
679
+ s += "\n"
680
+ end
681
+
682
+ # this method shows all the divs availble in the document
683
+ def show_divs
684
+ divs = document.getElementsByTagName("DIV")
685
+ puts "Found #{divs.length} div tags"
686
+ index = 1
687
+ divs.each do |d|
688
+ puts "#{index} id=#{d.invoke('id')} class=#{d.invoke("className")}"
689
+ index += 1
690
+ end
691
+ end
692
+
693
+ # this method is used to show all the tables that are available
694
+ def show_tables
695
+ tables = document.getElementsByTagName("TABLE")
696
+ puts "Found #{tables.length} tables"
697
+ index = 1
698
+ tables.each do |d|
699
+ puts "#{index} id=#{d.invoke('id')} rows=#{d.rows.length} columns=#{begin d.rows["0"].cells.length; rescue; end}"
700
+ index += 1
701
+ end
702
+ end
703
+
704
+ def show_pres
705
+ pres = document.getElementsByTagName("PRE")
706
+ puts "Found #{ pres.length } pre tags"
707
+ index = 1
708
+ pres.each do |d|
709
+ puts "#{index} id=#{d.invoke('id')} class=#{d.invoke("className")}"
710
+ index+=1
711
+ end
712
+ end
713
+
714
+ # this method shows all the spans availble in the document
715
+ def show_spans
716
+ spans = document.getElementsByTagName("SPAN")
717
+ puts "Found #{spans.length} span tags"
718
+ index = 1
719
+ spans.each do |d|
720
+ puts "#{index} id=#{d.invoke('id')} class=#{d.invoke("className")}"
721
+ index += 1
722
+ end
723
+ end
724
+
725
+ def show_labels
726
+ labels = document.getElementsByTagName("LABEL")
727
+ puts "Found #{labels.length} label tags"
728
+ index = 1
729
+ labels.each do |d|
730
+ puts "#{index} text=#{d.invoke('innerText')} class=#{d.invoke("className")} for=#{d.invoke("htmlFor")}"
731
+ index += 1
732
+ end
733
+ end
734
+
735
+ # Gives focus to the frame
736
+ def focus
737
+ active_element = document.activeElement
738
+ active_element.blur unless active_element.tagName == "BODY"
739
+ document.focus
740
+ end
741
+
742
+ def attach_command
743
+ "Watir::IE.attach(:hwnd, #{hwnd})"
744
+ end
745
+
746
+ private
747
+
748
+ def create_browser_window
749
+ @ie = WIN32OLE.new('InternetExplorer.Application')
750
+ end
751
+
752
+ def attach_browser_window how, what
753
+ log "Seeking Window with #{how}: #{what}"
754
+ ieTemp = nil
755
+ begin
756
+ Watir::until_with_timeout do
757
+ ieTemp = IE._find how, what
758
+ end
759
+ rescue Watir::Wait::TimeoutError
760
+ raise NoMatchingWindowFoundException,
761
+ "Unable to locate a window with #{how} of #{what}"
762
+ end
763
+ @ie = ieTemp
764
+ end
765
+
766
+ end # class IE
767
+ end