watir-classic 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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