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,21 @@
1
+ # http://www.vbcity.com/forums/topic.asp?tid=108859
2
+ require 'watir-classic/ie'
3
+ module Watir
4
+ module PageContainer
5
+ include Win32
6
+ def enabled_popup(timeout=4)
7
+ # Use handle of our parent window to see if we have any currently
8
+ # enabled popup.
9
+ hwnd_modal = 0
10
+ Wait.until(timeout) do
11
+ hwnd_modal, arr = GetWindow.call(hwnd, GW_ENABLEDPOPUP)
12
+ hwnd_modal > 0
13
+ end
14
+ # use hwnd() method to find the IE or Container hwnd (overriden by IE)
15
+ if hwnd_modal == hwnd() || 0 == hwnd_modal
16
+ hwnd_modal = nil
17
+ end
18
+ hwnd_modal
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,27 @@
1
+ # based on http://svn.instiki.org/instiki/trunk/test/watir/e2e.rb
2
+ # and http://rubyforge.org/pipermail/wtr-general/2005-November/004108.html
3
+
4
+ require 'watir-classic/ie-process'
5
+
6
+ class IEProcess < Watir::IE::Process
7
+ def stop
8
+ right_to_terminate_process = 1
9
+ handle = Win32API.new('kernel32.dll', 'OpenProcess', 'lil', 'l').
10
+ call(right_to_terminate_process, 0, @process_id)
11
+ Win32API.new('kernel32.dll', 'TerminateProcess', 'll', 'l').call(handle, 0)
12
+ end
13
+
14
+ end
15
+
16
+ module Watir
17
+ class IE
18
+ def process_id
19
+ @process_id ||= IEProcess.process_id_from_hwnd @ie.hwnd
20
+ end
21
+ attr_writer :process_id
22
+ def kill
23
+ iep = IEProcess.new process_id
24
+ iep.stop
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,29 @@
1
+ # This module includes checkers which are run on every page load
2
+ #
3
+ # In order to use this module, add a require to one of your test scripts:
4
+ # require 'watir-classic/contrib/page_checker'
5
+ # To add checkers, call the ie.add_checker method
6
+ #
7
+ # ie.add_checker(PageCheckers::NAVIGATION_CHECKER)
8
+ #
9
+ # Checkers are Ruby proc objects which are called within Watir::IE and passed
10
+ # the current instance of ie.
11
+
12
+ module PageCheckers
13
+
14
+ # This checker iterates through the current document including any frames
15
+ # and checks for http errors, 404, 500 etc
16
+ NAVIGATION_CHECKER = lambda do |ie|
17
+ if ie.document.frames.length > 1
18
+ 1.upto ie.document.frames.length do |i|
19
+ begin
20
+ ie.frame(:index, i).check_for_http_error
21
+ rescue Watir::Exception::UnknownFrameException
22
+ # frame can be already destroyed
23
+ end
24
+ end
25
+ else
26
+ ie.check_for_http_error
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,80 @@
1
+ require "uri"
2
+
3
+ module Watir
4
+ class Cookies
5
+ include Enumerable
6
+
7
+ def initialize(page_container)
8
+ @page_container = page_container
9
+ end
10
+
11
+ def each
12
+ @page_container.document.cookie.split(";").each do |cookie|
13
+ name, value = cookie.strip.split("=")
14
+ yield({:name => name, :value => value})
15
+ end
16
+ end
17
+
18
+ def add name, value, options={}
19
+ options = options.map do |option|
20
+ k, v = option
21
+ if k == :expires
22
+ "#{k}=#{v.gmtime.strftime("%a, %d %b %Y %H:%M:%S UTC")}"
23
+ elsif k == :secure
24
+ "secure" if v
25
+ else
26
+ "#{k}=#{v}"
27
+ end
28
+ end.compact.join("; ")
29
+
30
+ options = "; #{options}" unless options.empty?
31
+ @page_container.document.cookie = "#{name}=#{value}#{options}"
32
+ end
33
+
34
+ def delete name
35
+ options = {:expires => ::Time.now - 60 * 60 * 24}
36
+ delete_with_options name, options
37
+
38
+ # make sure that the cookie gets deleted
39
+ # there's got to be some easier way to do this
40
+ uri = URI.parse(@page_container.url)
41
+ domain = uri.host
42
+
43
+ paths = uri.path.split("/").reduce([]) do |paths, path|
44
+ paths << "#{paths.last}/#{path}".squeeze("/")
45
+ end << "/"
46
+
47
+ subdomains = domain.split(".").reverse.reduce([]) do |subdomains, part|
48
+ subdomain = "#{part}#{subdomains.last}"
49
+ subdomain = "." + subdomain unless subdomain == domain
50
+ subdomains << subdomain
51
+ end
52
+
53
+ subdomains.each do |subdomain|
54
+ domain_options = options.merge :domain => subdomain
55
+ delete_with_options name, domain_options
56
+ delete_with_options name, domain_options.merge(:secure => true)
57
+
58
+ paths.each do |path|
59
+ path_options = options.merge :path => path
60
+ delete_with_options name, path_options
61
+ delete_with_options name, path_options.merge(:secure => true)
62
+
63
+ path_domain_options = domain_options.merge :path => path
64
+ delete_with_options name, path_domain_options
65
+ delete_with_options name, path_domain_options.merge(:secure => true)
66
+ end
67
+ end
68
+ end
69
+
70
+ def clear
71
+ each {|cookie| delete cookie[:name]}
72
+ end
73
+
74
+ def delete_with_options name, options={}
75
+ add name, nil, options
76
+ end
77
+
78
+ private :delete_with_options
79
+ end
80
+ end
@@ -0,0 +1,43 @@
1
+ # these require statements are needed for Watir
2
+ # to work with minimum functionality
3
+ require 'timeout'
4
+ require 'watir-classic/win32ole'
5
+
6
+ # these are required already in commonwatir-classic, but not when using click_no_wait!
7
+ require 'watir-classic/util'
8
+ require 'watir-classic/exceptions'
9
+ require 'watir-classic/matches'
10
+ require 'watir-classic/wait'
11
+ require 'watir-classic/wait_helper'
12
+ require 'watir-classic/element_extensions'
13
+
14
+ require 'logger'
15
+ require 'watir-classic/logger'
16
+ require 'watir-classic/container'
17
+ require 'watir-classic/xpath_locator'
18
+ require 'watir-classic/locator'
19
+ require 'watir-classic/page-container'
20
+ require 'watir-classic/ie-class'
21
+ require 'watir-classic/drag_and_drop_helper'
22
+ require 'watir-classic/element'
23
+ require 'watir-classic/element_collection'
24
+ require 'watir-classic/form'
25
+ require 'watir-classic/frame'
26
+ require 'watir-classic/input_elements'
27
+ require 'watir-classic/non_control_elements'
28
+ require 'watir-classic/table'
29
+ require 'watir-classic/image'
30
+ require 'watir-classic/link'
31
+ require 'watir-classic/window'
32
+ require 'watir-classic/cookies'
33
+
34
+ require 'watir-classic/win32'
35
+ require 'watir-classic/modal_dialog'
36
+
37
+ require 'watir-classic/module'
38
+
39
+ require 'rautomation'
40
+ require 'watir-classic/dialogs/file_field'
41
+ require 'watir-classic/dialogs/javascript'
42
+
43
+ require 'watir-classic/supported_elements'
@@ -0,0 +1,34 @@
1
+ module Watir
2
+ class FileField < InputElement
3
+ def set(file_path)
4
+ assert_file_exists(file_path)
5
+ assert_exists
6
+ click_no_wait
7
+ set_file_name file_path.gsub(File::SEPARATOR, File::ALT_SEPARATOR)
8
+ open_button.click
9
+ end
10
+
11
+ alias_method :value=, :set
12
+
13
+ def assert_file_exists(file_path)
14
+ raise Errno::ENOENT, "#{file_path} has to exist to set!" unless File.exists?(file_path)
15
+ end
16
+
17
+ def set_file_name(path_to_file)
18
+ file_upload_window.text_field(:class => 'Edit', :index => 0).set path_to_file
19
+ end
20
+
21
+ def open_button
22
+ file_upload_window.button(:value => /&Open|&Abrir/)
23
+ end
24
+
25
+ def cancel_button
26
+ file_upload_window.button(:value => /Cancel/)
27
+ end
28
+
29
+ def file_upload_window
30
+ @window ||= RAutomation::Window.new(:title => /^choose file( to upload)?|Elegir archivos para cargar$/i)
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,43 @@
1
+ module Watir
2
+
3
+ class JavascriptDialog
4
+ WINDOW_TITLES = ['Message from webpage', 'Windows Internet Explorer','Microsoft Internet Explorer',/Mensaje de p.*/]
5
+
6
+ def initialize(opts={})
7
+ @opts = opts
8
+ end
9
+
10
+ def exists?
11
+ javascript_dialog_window.exists?
12
+ end
13
+
14
+ def button(value)
15
+ javascript_dialog_window.button(:value => value)
16
+ end
17
+
18
+ def close
19
+ javascript_dialog_window.close
20
+ end
21
+
22
+ def text
23
+ javascript_dialog_window.text
24
+ end
25
+
26
+ def javascript_dialog_window
27
+ @window ||= ::RAutomation::Window.new(:title => @opts[:title] || /^(#{WINDOW_TITLES.join('|')})$/)
28
+ end
29
+
30
+ Watir::Container.module_eval do
31
+ def javascript_dialog(opts={})
32
+ JavascriptDialog.new(opts)
33
+ end
34
+
35
+ alias_method :dialog, :javascript_dialog
36
+ end
37
+ end
38
+ end
39
+
40
+
41
+
42
+
43
+
@@ -0,0 +1,68 @@
1
+ module Watir
2
+ module DragAndDropHelper
3
+
4
+ def drag_and_drop_on(target)
5
+ perform_action do
6
+ assert_target target
7
+ drop_x, drop_y = target.send :source_x_y
8
+ drag_to drop_x, drop_y
9
+ end
10
+ end
11
+
12
+ def drag_and_drop_by(distance_x, distance_y)
13
+ perform_action do
14
+ drag_x, drag_y = source_x_y
15
+ drag_to drag_x + distance_x, drag_y + distance_y
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def drag_to(drop_x, drop_y)
22
+ drag_x, drag_y = source_x_y
23
+ mouse = page_container.rautomation.mouse
24
+ mouse.move :x => drag_x , :y => drag_y
25
+ mouse.press
26
+ mouse.move :x => drop_x, :y => drop_y
27
+ mouse.release
28
+ end
29
+
30
+ def source_x_y
31
+ center_x_y_absolute left_edge_absolute, top_edge_absolute
32
+ end
33
+
34
+ def assert_target(target)
35
+ target.assert_exists
36
+ target.assert_enabled
37
+ end
38
+
39
+ def top_edge
40
+ ole_object.getBoundingClientRect(0).top.to_i
41
+ end
42
+
43
+ def top_edge_absolute
44
+ top_edge + page_container.document.parentWindow.screenTop.to_i
45
+ end
46
+
47
+ def left_edge
48
+ ole_object.getBoundingClientRect(0).left.to_i
49
+ end
50
+
51
+ def left_edge_absolute
52
+ left_edge + page_container.document.parentWindow.screenLeft.to_i
53
+ end
54
+
55
+ def right_edge
56
+ ole_object.getBoundingClientRect(0).right.to_i
57
+ end
58
+
59
+ def bottom_edge
60
+ ole_object.getBoundingClientRect(0).bottom.to_i
61
+ end
62
+
63
+ def center_x_y_absolute x, y
64
+ return (right_edge - left_edge) / 2 + x, (bottom_edge - top_edge) / 2 + y
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,438 @@
1
+ module Watir
2
+ # Base class for html elements.
3
+ # This is not a class that users would normally access.
4
+ class Element # Wrapper
5
+ include Comparable
6
+ include ElementExtensions
7
+ include Exception
8
+ include Container # presumes @container is defined
9
+ include DragAndDropHelper
10
+
11
+ attr_accessor :container
12
+
13
+ # number of spaces that separate the property from the value in the to_s method
14
+ TO_S_SIZE = 14
15
+
16
+ def initialize(container, specifiers)
17
+ set_container container
18
+ raise ArgumentError, "#{specifiers.inspect} has to be Hash" unless specifiers.is_a?(Hash)
19
+
20
+ @o = specifiers[:ole_object]
21
+ @specifiers = specifiers
22
+ end
23
+
24
+ def <=> other
25
+ assert_exists
26
+ other.assert_exists
27
+ ole_object.sourceindex <=> other.ole_object.sourceindex
28
+ end
29
+
30
+ alias_method :eql?, :==
31
+
32
+ def locate
33
+ @o = @container.locator_for(TaggedElementLocator, @specifiers, self.class).locate
34
+ end
35
+
36
+ # Return the ole object, allowing any methods of the DOM that Watir doesn't support to be used.
37
+ def ole_object
38
+ @o
39
+ end
40
+
41
+ def ole_object=(o)
42
+ @o = o
43
+ end
44
+
45
+ def inspect
46
+ '#<%s:0x%x located=%s specifiers=%s>' % [self.class, hash*2, !!ole_object, @specifiers.inspect]
47
+ end
48
+
49
+ private
50
+
51
+ def self.attr_ole(method_name, ole_method_name=nil)
52
+ class_eval %Q[
53
+ def #{method_name}
54
+ assert_exists
55
+ ole_method_name = '#{ole_method_name || method_name.to_s.gsub(/\?$/, '')}'
56
+ ole_object.invoke(ole_method_name) rescue attribute_value(ole_method_name) || '' rescue ''
57
+ end]
58
+ end
59
+
60
+ public
61
+
62
+ def assert_exists
63
+ locate
64
+ unless ole_object
65
+ exception_class = self.is_a?(Frame) ? UnknownFrameException : UnknownObjectException
66
+ raise exception_class.new(Watir::Exception.message_for_unable_to_locate(@specifiers))
67
+ end
68
+ end
69
+
70
+ def assert_enabled
71
+ raise ObjectDisabledException, "object #{@specifiers.inspect} is disabled" unless enabled?
72
+ end
73
+
74
+ # return the id of the element
75
+ attr_ole :id
76
+ # return the title of the element
77
+ attr_ole :title
78
+ # return the class name of the element
79
+ # raise an ObjectNotFound exception if the object cannot be found
80
+ attr_ole :class_name, :className
81
+ # return the unique COM number for the element
82
+ attr_ole :unique_number, :uniqueNumber
83
+ # Return the outer html of the object - see http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/outerhtml.asp?frame=true
84
+ attr_ole :html, :outerHTML
85
+
86
+ def tag_name
87
+ assert_exists
88
+ @o.tagName.downcase
89
+ end
90
+
91
+ # returns specific Element subclass for current Element
92
+ def to_subtype
93
+ assert_exists
94
+
95
+ tag = tag_name
96
+ if tag == "html"
97
+ element(:ole_object => ole_object)
98
+ elsif tag == "input"
99
+ send(ole_object.invoke('type'), :ole_object => ole_object)
100
+ elsif tag == "select"
101
+ select_list(:ole_object => ole_object)
102
+ elsif respond_to?(tag.downcase)
103
+ send(tag.downcase, :ole_object => ole_object)
104
+ else
105
+ self
106
+ end
107
+ end
108
+
109
+ # send keys to element
110
+ def send_keys(*keys)
111
+ focus
112
+ page_container.send_keys *keys
113
+ end
114
+
115
+ # return the css style as a string
116
+ def style
117
+ assert_exists
118
+ ole_object.style.cssText
119
+ end
120
+
121
+ # Return the innerText of the object or an empty string if the object is
122
+ # not visible
123
+ # Raise an ObjectNotFound exception if the object cannot be found
124
+ def text
125
+ assert_exists
126
+ visible? ? ole_object.innerText.strip : ""
127
+ end
128
+
129
+ def __ole_inner_elements
130
+ assert_exists
131
+ ole_object.all
132
+ end
133
+
134
+ def document
135
+ assert_exists
136
+ ole_object
137
+ end
138
+
139
+ # Return the element immediately containing self.
140
+ def parent
141
+ assert_exists
142
+ parent_element = ole_object.parentelement
143
+ return unless parent_element
144
+ Element.new(self, :ole_object => parent_element).to_subtype
145
+ end
146
+
147
+ def typingspeed
148
+ @container.typingspeed
149
+ end
150
+
151
+ def type_keys
152
+ @type_keys || @container.type_keys
153
+ end
154
+
155
+ def activeObjectHighLightColor
156
+ @container.activeObjectHighLightColor
157
+ end
158
+
159
+ # Return an array with many of the properties, in a format to be used by the to_s method
160
+ def string_creator
161
+ n = []
162
+ n << "id:".ljust(TO_S_SIZE) + self.id.to_s
163
+ return n
164
+ end
165
+
166
+ private :string_creator
167
+
168
+ # Display basic details about the object. Sample output for a button is shown.
169
+ # Raises UnknownObjectException if the object is not found.
170
+ # name b4
171
+ # type button
172
+ # id b5
173
+ # value Disabled Button
174
+ # disabled true
175
+ def to_s
176
+ assert_exists
177
+ return string_creator.join("\n")
178
+ end
179
+
180
+ # This method is responsible for setting and clearing the colored highlighting on the currently active element.
181
+ # use :set to set the highlight
182
+ # :clear to clear the highlight
183
+ # TODO: Make this two methods: set_highlight & clear_highlight
184
+ # TODO: Remove begin/rescue blocks
185
+ def highlight(set_or_clear)
186
+ if set_or_clear == :set
187
+ begin
188
+ @original_color ||= ole_object.style.backgroundColor
189
+ ole_object.style.backgroundColor = @container.activeObjectHighLightColor
190
+ rescue
191
+ @original_color = nil
192
+ end
193
+ else
194
+ begin
195
+ ole_object.style.backgroundColor = @original_color if @original_color
196
+ rescue
197
+ # we could be here for a number of reasons...
198
+ # e.g. page may have reloaded and the reference is no longer valid
199
+ ensure
200
+ @original_color = nil
201
+ end
202
+ end
203
+ end
204
+
205
+ private :highlight
206
+
207
+ # This method clicks the active element.
208
+ # raises: UnknownObjectException if the object is not found
209
+ # ObjectDisabledException if the object is currently disabled
210
+ def click
211
+ click!
212
+ @container.wait
213
+ end
214
+
215
+ def right_click
216
+ perform_action {fire_event("oncontextmenu"); @container.wait}
217
+ end
218
+
219
+ def double_click
220
+ perform_action {fire_event("ondblclick"); @container.wait}
221
+ end
222
+
223
+ def replace_method(method)
224
+ method == 'click' ? 'click!' : method
225
+ end
226
+
227
+ private :replace_method
228
+
229
+ def build_method(method_name, *args)
230
+ arguments = args.map do |argument|
231
+ if argument.is_a?(String)
232
+ argument = "'#{argument}'"
233
+ else
234
+ argument = argument.inspect
235
+ end
236
+ end
237
+ "#{replace_method(method_name)}(#{arguments.join(',')})"
238
+ end
239
+
240
+ private :build_method
241
+
242
+ def generate_ruby_code(element, method_name, *args)
243
+ # needs to be done like this to avoid segfault on ruby 1.9.3
244
+ tag_name = @specifiers[:tag_name].join("' << '")
245
+ element = "#{self.class}.new(#{@page_container.attach_command}, :tag_name => Array.new << '#{tag_name}', :unique_number => #{unique_number})"
246
+ method = build_method(method_name, *args)
247
+ ruby_code = "$:.unshift(#{$LOAD_PATH.map {|p| "'#{p}'" }.join(").unshift(")});" <<
248
+ "require '#{File.expand_path(File.dirname(__FILE__))}/core';#{element}.#{method};"
249
+ ruby_code
250
+ end
251
+
252
+ private :generate_ruby_code
253
+
254
+ def spawned_no_wait_command(command)
255
+ command = "-e #{command.inspect}"
256
+ unless $DEBUG
257
+ "start rubyw #{command}"
258
+ else
259
+ puts "#no_wait command:"
260
+ command = "ruby #{command}"
261
+ puts command
262
+ command
263
+ end
264
+ end
265
+
266
+ private :spawned_no_wait_command
267
+
268
+ def click!
269
+ perform_action do
270
+ # Not sure why but in IE9 Document mode, passing a parameter
271
+ # to click seems to work. Firing the onClick event breaks other tests
272
+ # so this seems to be the safest change and also works fine in IE8
273
+ ole_object.click(0)
274
+ end
275
+ end
276
+
277
+ # Flash the element the specified number of times.
278
+ # Defaults to 10 flashes.
279
+ def flash number=10
280
+ assert_exists
281
+ number.times do
282
+ highlight(:set)
283
+ sleep 0.05
284
+ highlight(:clear)
285
+ sleep 0.05
286
+ end
287
+ nil
288
+ end
289
+
290
+ # Executes a user defined "fireEvent" for objects with JavaScript events tied to them such as DHTML menus.
291
+ # usage: allows a generic way to fire javascript events on page objects such as "onMouseOver", "onClick", etc.
292
+ # raises: UnknownObjectException if the object is not found
293
+ # ObjectDisabledException if the object is currently disabled
294
+ def fire_event(event)
295
+ perform_action {dispatch_event(event); @container.wait}
296
+ end
297
+
298
+ def dispatch_event(event)
299
+ assert_exists
300
+
301
+ if IE.version_parts.first.to_i >= 9 && container.page_container.document.documentMode.to_i >= 9
302
+ ole_object.dispatchEvent(create_event(event))
303
+ else
304
+ ole_object.fireEvent(event)
305
+ end
306
+ end
307
+
308
+ def create_event(event)
309
+ event =~ /on(.*)/i
310
+ event = $1 if $1
311
+ event.downcase!
312
+ # See http://www.howtocreate.co.uk/tutorials/javascript/domevents
313
+ case event
314
+ when 'abort', 'blur', 'change', 'error', 'focus', 'load',
315
+ 'reset', 'resize', 'scroll', 'select', 'submit', 'unload'
316
+ event_name = :initEvent
317
+ event_type = 'HTMLEvents'
318
+ event_args = [event, true, true]
319
+ when 'select'
320
+ event_name = :initUIEvent
321
+ event_type = 'UIEvent'
322
+ event_args = [event, true, true, @container.page_container.document.parentWindow.window,0]
323
+ when 'keydown', 'keypress', 'keyup'
324
+ event_name = :initKeyboardEvent
325
+ event_type = 'KeyboardEvent'
326
+ # 'type', bubbles, cancelable, windowObject, ctrlKey, altKey, shiftKey, metaKey, keyCode, charCode
327
+ event_args = [event, true, true, @container.page_container.document.parentWindow.window, false, false, false, false, 0, 0]
328
+ when 'click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup',
329
+ 'contextmenu', 'drag', 'dragstart', 'dragenter', 'dragover', 'dragleave', 'dragend', 'drop', 'selectstart'
330
+ event_name = :initMouseEvent
331
+ event_type = 'MouseEvents'
332
+ # 'type', bubbles, cancelable, windowObject, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget
333
+ event_args = [event, true, true, @container.page_container.document.parentWindow.window, 1, 0, 0, 0, 0, false, false, false, false, 0, @container.page_container.document]
334
+ else
335
+ raise UnhandledEventException, "Don't know how to trigger event '#{event}'"
336
+ end
337
+ event = @container.page_container.document.createEvent(event_type)
338
+ event.send event_name, *event_args
339
+ event
340
+ end
341
+
342
+ # This method sets focus on the active element.
343
+ # raises: UnknownObjectException if the object is not found
344
+ # ObjectDisabledException if the object is currently disabled
345
+ def focus
346
+ assert_exists
347
+ assert_enabled
348
+ @page_container.focus
349
+ ole_object.focus(0)
350
+ end
351
+
352
+ def focused?
353
+ assert_exists
354
+ assert_enabled
355
+ @page_container.document.activeElement.uniqueNumber == unique_number
356
+ end
357
+
358
+ # Returns whether this element actually exists.
359
+ def exists?
360
+ begin
361
+ locate
362
+ rescue WIN32OLERuntimeError, UnknownObjectException
363
+ @o = nil
364
+ end
365
+ !!@o
366
+ end
367
+
368
+ alias :exist? :exists?
369
+
370
+ # Returns true if the element is enabled, false if it isn't.
371
+ # raises: UnknownObjectException if the object is not found
372
+ def enabled?
373
+ assert_exists
374
+ !disabled?
375
+ end
376
+
377
+ def disabled?
378
+ assert_exists
379
+ false
380
+ end
381
+
382
+ # If any parent element isn't visible then we cannot write to the
383
+ # element. The only realiable way to determine this is to iterate
384
+ # up the DOM element tree checking every element to make sure it's
385
+ # visible.
386
+ def visible?
387
+ # Now iterate up the DOM element tree and return false if any
388
+ # parent element isn't visible
389
+ assert_exists
390
+ object = @o
391
+ while object
392
+ begin
393
+ if object.currentstyle.invoke('visibility') =~ /^hidden$/i
394
+ return false
395
+ end
396
+ if object.currentstyle.invoke('display') =~ /^none$/i
397
+ return false
398
+ end
399
+ rescue WIN32OLERuntimeError
400
+ end
401
+ object = object.parentElement
402
+ end
403
+ true
404
+ end
405
+
406
+ # Get attribute value for any attribute of the element.
407
+ # Returns null if attribute doesn't exist.
408
+ def attribute_value(attribute_name)
409
+ assert_exists
410
+ ole_object.getAttribute(attribute_name)
411
+ end
412
+
413
+ def perform_action
414
+ assert_exists
415
+ assert_enabled
416
+ highlight(:set)
417
+ yield
418
+ highlight(:clear)
419
+ end
420
+
421
+ private :perform_action
422
+
423
+ def method_missing(method_name, *args, &block)
424
+ meth = method_name.to_s
425
+ if meth =~ /(.*)_no_wait/ && self.respond_to?($1)
426
+ perform_action do
427
+ ruby_code = generate_ruby_code(self, $1, *args)
428
+ system(spawned_no_wait_command(ruby_code))
429
+ end
430
+ elsif meth =~ /^data_(.*)/
431
+ self.send(:attribute_value, meth.gsub("_", "-")) || ''
432
+ else
433
+ super
434
+ end
435
+ end
436
+
437
+ end
438
+ end