watir-classic 3.3.0 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/CHANGES +17 -0
  2. data/Gemfile.lock +9 -8
  3. data/LICENSE +1 -0
  4. data/README.rdoc +6 -7
  5. data/Rakefile +3 -1
  6. data/VERSION +1 -1
  7. data/lib/watir-classic.rb +0 -5
  8. data/lib/watir-classic/browser.rb +58 -35
  9. data/lib/watir-classic/browsers.rb +1 -1
  10. data/lib/watir-classic/container.rb +39 -33
  11. data/lib/watir-classic/cookies.rb +32 -2
  12. data/lib/watir-classic/core.rb +0 -1
  13. data/lib/watir-classic/dialogs/alert.rb +12 -0
  14. data/lib/watir-classic/dialogs/file_field.rb +11 -0
  15. data/lib/watir-classic/drag_and_drop_helper.rb +14 -0
  16. data/lib/watir-classic/element.rb +292 -257
  17. data/lib/watir-classic/element_collection.rb +26 -8
  18. data/lib/watir-classic/element_extensions.rb +22 -16
  19. data/lib/watir-classic/exceptions.rb +4 -4
  20. data/lib/watir-classic/form.rb +52 -49
  21. data/lib/watir-classic/frame.rb +23 -14
  22. data/lib/watir-classic/ie-class.rb +363 -315
  23. data/lib/watir-classic/ie-process.rb +1 -0
  24. data/lib/watir-classic/ie.rb +0 -17
  25. data/lib/watir-classic/image.rb +58 -64
  26. data/lib/watir-classic/input_elements.rb +224 -219
  27. data/lib/watir-classic/link.rb +14 -15
  28. data/lib/watir-classic/locator.rb +12 -7
  29. data/lib/watir-classic/matches.rb +7 -3
  30. data/lib/watir-classic/modal_dialog.rb +38 -26
  31. data/lib/watir-classic/non_control_elements.rb +29 -0
  32. data/lib/watir-classic/options.rb +10 -15
  33. data/lib/watir-classic/page-container.rb +30 -48
  34. data/lib/watir-classic/process.rb +4 -2
  35. data/lib/watir-classic/screenshot.rb +6 -0
  36. data/lib/watir-classic/supported_elements.rb +36 -14
  37. data/lib/watir-classic/table.rb +81 -71
  38. data/lib/watir-classic/util.rb +9 -11
  39. data/lib/watir-classic/wait.rb +17 -4
  40. data/lib/watir-classic/wait_helper.rb +15 -2
  41. data/lib/watir-classic/win32.rb +2 -1
  42. data/lib/watir-classic/window.rb +35 -7
  43. data/lib/watir-classic/xpath_locator.rb +1 -0
  44. data/lib/watir-classic/yard/global_macros.rb +7 -0
  45. data/spec/frame_spec.rb +17 -0
  46. metadata +5 -7
  47. data/lib/watir-classic/close_all.rb +0 -31
  48. data/lib/watir-classic/contrib/enabled_popup.rb +0 -21
  49. data/lib/watir-classic/contrib/ie-new-process.rb +0 -27
  50. data/lib/watir-classic/contrib/page_checker.rb +0 -29
  51. data/watir.gif +0 -0
@@ -1,6 +1,7 @@
1
1
  require "uri"
2
2
 
3
3
  module Watir
4
+ # Returned by {IE#cookies}.
4
5
  class Cookies
5
6
  include Enumerable
6
7
 
@@ -8,6 +9,15 @@ module Watir
8
9
  @page_container = page_container
9
10
  end
10
11
 
12
+ # Iterate over each cookie.
13
+ #
14
+ # @example
15
+ # browser.cookies.each do |cookie|
16
+ # puts cookie[:name]
17
+ # puts cookie[:value]
18
+ # end
19
+ #
20
+ # @yieldparam [Hash] cookie name and value pair of the cookie.
11
21
  def each
12
22
  @page_container.document.cookie.split(";").each do |cookie|
13
23
  name, value = cookie.strip.split("=")
@@ -15,7 +25,21 @@ module Watir
15
25
  end
16
26
  end
17
27
 
18
- def add name, value, options={}
28
+ # Add a cookie.
29
+ #
30
+ # @example Add a cookie with default options:
31
+ # browser.cookies.add "name", "value'
32
+ #
33
+ # @example Add a cookie with options:
34
+ # browser.cookie.add "name", "value", :expires => Time.now, :secure => true, :path => "/foo/bar"
35
+ #
36
+ # @param [String] name name of the cookie.
37
+ # @param [String] value value of the cookie.
38
+ # @param [Hash] options options for the cookie.
39
+ # @option options [Time] :expires Expiration time.
40
+ # @option options [Boolean] :secure (false) Secure flag. Set when value is true.
41
+ # @option options [String] :path Path for cookie.
42
+ def add(name, value, options={})
19
43
  options = options.map do |option|
20
44
  k, v = option
21
45
  if k == :expires
@@ -31,7 +55,12 @@ module Watir
31
55
  @page_container.document.cookie = "#{name}=#{value}#{options}"
32
56
  end
33
57
 
34
- def delete name
58
+ # Delete a cookie.
59
+ #
60
+ # @note does not raise any exceptions when cookie with the specified name is not found.
61
+ #
62
+ # @param [String] name Cookie with the specified name to be deleted.
63
+ def delete(name)
35
64
  options = {:expires => ::Time.now - 60 * 60 * 24}
36
65
  delete_with_options name, options
37
66
 
@@ -68,6 +97,7 @@ module Watir
68
97
  end
69
98
  end
70
99
 
100
+ # Delete all cookies for the page.
71
101
  def clear
72
102
  each {|cookie| delete cookie[:name]}
73
103
  end
@@ -3,7 +3,6 @@
3
3
  require 'timeout'
4
4
  require 'watir-classic/win32ole'
5
5
 
6
- # these are required already in commonwatir-classic, but not when using click_no_wait!
7
6
  require 'watir-classic/util'
8
7
  require 'watir-classic/exceptions'
9
8
  require 'watir-classic/matches'
@@ -1,33 +1,45 @@
1
1
  module Watir
2
+ # Handle different JavaScript dialogs (alert, prompt and confirm).
3
+ # Returned by {Container#alert}.
2
4
  class Alert
3
5
  include ElementExtensions
4
6
 
7
+ # JavaScript dialog titles to search for.
8
+ #
9
+ # @example When the title of your IE dialog is missing, add a new one:
10
+ # Watir::Alert::WINDOW_TITLES << "My missing title"
5
11
  WINDOW_TITLES = ['Message from webpage', 'Windows Internet Explorer', 'Microsoft Internet Explorer', /Mensaje de p.*/, "Explorer User Prompt"]
6
12
 
7
13
  def initialize(container)
8
14
  @container = container
9
15
  end
10
16
 
17
+ # @return [Boolean] true when JavaScript dialog exists and is visible, false otherwise.
11
18
  def exists?
12
19
  dialog.present?
13
20
  end
14
21
 
15
22
  alias_method :present?, :exists?
16
23
 
24
+ # Close the JavaScript dialog.
17
25
  def close
18
26
  dialog.close
19
27
  wait_until_not_exists
20
28
  end
21
29
 
30
+ # @return [String] the visible text of the JavaScript dialog.
22
31
  def text
23
32
  dialog.text
24
33
  end
25
34
 
35
+ # Press the "OK" button on the JavaScript dialog.
26
36
  def ok
27
37
  dialog.button(:value => "OK").click
28
38
  wait_until_not_exists
29
39
  end
30
40
 
41
+ # Set the text on the JavaScript prompt dialog.
42
+ # @param [String] text text to set.
31
43
  def set(text)
32
44
  dialog.text_field.set text
33
45
  end
@@ -1,5 +1,14 @@
1
1
  module Watir
2
+ # Returned by {Container#file_field}.
2
3
  class FileField < InputElement
4
+ # Set the path of the file field.
5
+ #
6
+ # @example
7
+ # browser.file_field.set("c:/foo/bar.txt")
8
+ #
9
+ # @param [String] file_path absolute path to existing file.
10
+ # @macro exists
11
+ # @raise [Errno::ENOENT] when file does not exist.
3
12
  def set(file_path)
4
13
  assert_file_exists(file_path)
5
14
  assert_exists
@@ -11,6 +20,8 @@ module Watir
11
20
 
12
21
  alias_method :value=, :set
13
22
 
23
+ private
24
+
14
25
  def assert_file_exists(file_path)
15
26
  raise Errno::ENOENT, "#{file_path} has to exist to set!" unless File.exists?(file_path)
16
27
  end
@@ -1,6 +1,14 @@
1
1
  module Watir
2
+ # This module has methods for performing drag & drop functionality. It is
3
+ # included into {Element} making each element draggable if they're draggable
4
+ # on the web page too.
2
5
  module DragAndDropHelper
3
6
 
7
+ # Drag and drop this element onto another element.
8
+ #
9
+ # @param [Element] target element to be dragged on.
10
+ # @macro exists
11
+ # @macro enabled
4
12
  def drag_and_drop_on(target)
5
13
  perform_action do
6
14
  assert_target target
@@ -12,6 +20,12 @@ module Watir
12
20
  end
13
21
  end
14
22
 
23
+ # Drag and drop this element by a distance.
24
+ #
25
+ # @param [Fixnum] distance_x distance to drag element on x-axis. Can be negative.
26
+ # @param [Fixnum] distance_y distance to drag element on y-axis. Can be negative.
27
+ # @macro exists
28
+ # @macro enabled
15
29
  def drag_and_drop_by(distance_x, distance_y)
16
30
  perform_action do
17
31
  drag_to do |mouse|
@@ -10,7 +10,36 @@ module Watir
10
10
 
11
11
  attr_accessor :container
12
12
 
13
+ class << self
14
+
15
+ private
16
+
17
+ # @!macro attr_ole
18
+ # @!method $1
19
+ # Retrieve element's $1 from the $2 OLE method.
20
+ # @see http://msdn.microsoft.com/en-us/library/hh773183(v=vs.85).aspx MSDN Documentation
21
+ # @return [String, Boolean, Fixnum] element's "$1" attribute value.
22
+ # Return type depends of the attribute type.
23
+ # @return [String] an empty String if the "$1" attribute does not exist.
24
+ # @macro exists
25
+ def attr_ole(method_name, ole_method_name=nil)
26
+ class_eval %Q[
27
+ def #{method_name}
28
+ assert_exists
29
+ ole_method_name = '#{ole_method_name || method_name.to_s.gsub(/\?$/, '')}'
30
+ ole_object.invoke(ole_method_name) rescue attribute_value(ole_method_name) || '' rescue ''
31
+ end]
32
+ end
33
+ end
34
+
35
+ attr_ole :id
36
+ attr_ole :title
37
+ attr_ole :class_name, :className
38
+ attr_ole :unique_number, :uniqueNumber
39
+ attr_ole :html, :outerHTML
40
+
13
41
  # number of spaces that separate the property from the value in the to_s method
42
+ # @private
14
43
  TO_S_SIZE = 14
15
44
 
16
45
  def initialize(container, specifiers)
@@ -29,66 +58,33 @@ module Watir
29
58
 
30
59
  alias_method :eql?, :==
31
60
 
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
61
+ # @return [WIN32OLE] OLE object of the element, allowing any methods of the DOM
62
+ # that Watir doesn't support to be used.
63
+ def ole_object
64
+ @o
65
+ end
44
66
 
45
67
  def inspect
46
68
  '#<%s:0x%x located=%s specifiers=%s>' % [self.class, hash*2, !!ole_object, @specifiers.inspect]
47
69
  end
48
70
 
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?
71
+ def to_s
72
+ assert_exists
73
+ string_creator.join("\n")
72
74
  end
73
75
 
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
-
76
+ # @return [String] element's html tag name in downcase.
77
+ # @macro exists
86
78
  def tag_name
87
79
  assert_exists
88
80
  @o.tagName.downcase
89
81
  end
90
82
 
91
- # returns specific Element subclass for current Element
83
+ # Cast {Element} into specific subclass.
84
+ # @example Convert div element to {Div} class:
85
+ # browser.element(:tag_name => "div").to_subtype # => Watir::Div
86
+ # @return {Element} element casted into specific sub-class of Element.
87
+ # @macro exists
92
88
  def to_subtype
93
89
  assert_exists
94
90
 
@@ -106,44 +102,46 @@ module Watir
106
102
  end
107
103
  end
108
104
 
109
- # send keys to element
105
+ # Send keys to the element
106
+ # @example
107
+ # browser.text_field.send_keys "hello", [:control, "a"], :backspace
108
+ # @param [String, Array<Symbol, String>, Symbol] keys Keys to send to the element.
109
+ # @see https://github.com/jarmo/RAutomation/blob/master/lib/rautomation/adapter/win_32/window.rb RAutomation::Window#send_keys documentation.
110
110
  def send_keys(*keys)
111
111
  focus
112
112
  page_container.send_keys *keys
113
113
  end
114
114
 
115
- # return the css style as a string
115
+ # Retrieve element's css style.
116
+ # @param [String] property When property is specified then only css for that property is returned.
117
+ # @return [String] css style as a one long String.
118
+ # @return [String] css style for specified property if property parameter is specified.
119
+ # @macro exists
116
120
  def style(property=nil)
117
121
  assert_exists
118
122
  css = ole_object.style.cssText
119
123
 
120
124
  if property
121
- properties = Hash[css.downcase.split(";").map { |p| p.split(":").map(&:strip) }]
122
- properties[property]
125
+ properties = Hash[css.downcase.split(";").map { |p| p.split(":").map(&:strip) }]
126
+ properties[property]
123
127
  else
124
128
  css
125
129
  end
126
130
  end
127
131
 
128
- # Return the innerText of the object or an empty string if the object is
129
- # not visible
130
- # Raise an ObjectNotFound exception if the object cannot be found
132
+ # The text value of the element between html tags.
133
+ # @return [String] element's text.
134
+ # @return [String] empty String when element is not visible.
135
+ # @macro exists
131
136
  def text
132
137
  assert_exists
133
138
  visible? ? ole_object.innerText.strip : ""
134
139
  end
135
140
 
136
- def __ole_inner_elements
137
- assert_exists
138
- ole_object.all
139
- end
140
-
141
- def document
142
- assert_exists
143
- ole_object
144
- end
145
-
146
- # Return the element immediately containing self.
141
+ # Retrieve the element immediately containing self.
142
+ # @return [Element] parent element of self.
143
+ # @return [Element] self when parent element does not exist.
144
+ # @macro exists
147
145
  def parent
148
146
  assert_exists
149
147
  parent_element = ole_object.parentelement
@@ -151,202 +149,58 @@ module Watir
151
149
  Element.new(self, :ole_object => parent_element).to_subtype
152
150
  end
153
151
 
154
- def typingspeed
155
- @container.typingspeed
156
- end
157
-
158
- def type_keys
159
- @type_keys || @container.type_keys
160
- end
161
-
162
- def activeObjectHighLightColor
163
- @container.activeObjectHighLightColor
164
- end
165
-
166
- # Return an array with many of the properties, in a format to be used by the to_s method
167
- def string_creator
168
- n = []
169
- n << "id:".ljust(TO_S_SIZE) + self.id.to_s
170
- return n
171
- end
172
-
173
- private :string_creator
174
-
175
- # Display basic details about the object. Sample output for a button is shown.
176
- # Raises UnknownObjectException if the object is not found.
177
- # name b4
178
- # type button
179
- # id b5
180
- # value Disabled Button
181
- # disabled true
182
- def to_s
183
- assert_exists
184
- return string_creator.join("\n")
185
- end
186
-
187
- # This method is responsible for setting and clearing the colored highlighting on the currently active element.
188
- # use :set to set the highlight
189
- # :clear to clear the highlight
190
- # TODO: Make this two methods: set_highlight & clear_highlight
191
- # TODO: Remove begin/rescue blocks
192
- def highlight(set_or_clear)
193
- if set_or_clear == :set
194
- begin
195
- @original_color ||= ole_object.style.backgroundColor
196
- ole_object.style.backgroundColor = @container.activeObjectHighLightColor
197
- rescue
198
- @original_color = nil
199
- end
200
- else
201
- begin
202
- ole_object.style.backgroundColor = @original_color if @original_color
203
- rescue
204
- # we could be here for a number of reasons...
205
- # e.g. page may have reloaded and the reference is no longer valid
206
- ensure
207
- @original_color = nil
208
- end
209
- end
210
- end
211
-
212
- private :highlight
213
-
214
- # This method clicks the active element.
215
- # raises: UnknownObjectException if the object is not found
216
- # ObjectDisabledException if the object is currently disabled
152
+ # Performs a left click on the element.
153
+ # Will wait automatically until browser is ready after the click if page load was triggered for example.
154
+ # @macro exists
155
+ # @macro enabled
217
156
  def click
218
157
  click!
219
158
  @container.wait
220
159
  end
221
160
 
161
+ # Performs a right click on the element.
162
+ # Will wait automatically until browser is ready after the click if page load was triggered for example.
163
+ # @macro enabled
164
+ # @macro exists
222
165
  def right_click
223
166
  perform_action {fire_event("oncontextmenu"); @container.wait}
224
167
  end
225
168
 
169
+ # Performs a double click on the element.
170
+ # Will wait automatically until browser is ready after the click if page load was triggered for example.
171
+ # @macro exists
172
+ # @macro enabled
226
173
  def double_click
227
174
  perform_action {fire_event("ondblclick"); @container.wait}
228
175
  end
229
176
 
230
- def replace_method(method)
231
- method == 'click' ? 'click!' : method
232
- end
233
-
234
- private :replace_method
235
-
236
- def build_method(method_name, *args)
237
- arguments = args.map do |argument|
238
- if argument.is_a?(String)
239
- argument = "'#{argument}'"
240
- else
241
- argument = argument.inspect
242
- end
243
- end
244
- "#{replace_method(method_name)}(#{arguments.join(',')})"
245
- end
246
-
247
- private :build_method
248
-
249
- def generate_ruby_code(element, method_name, *args)
250
- # needs to be done like this to avoid segfault on ruby 1.9.3
251
- tag_name = @specifiers[:tag_name].join("' << '")
252
- element = "#{self.class}.new(#{@page_container.attach_command}, :tag_name => Array.new << '#{tag_name}', :unique_number => #{unique_number})"
253
- method = build_method(method_name, *args)
254
- ruby_code = "$:.unshift(#{$LOAD_PATH.map {|p| "'#{p}'" }.join(").unshift(")});" <<
255
- "require '#{File.expand_path(File.dirname(__FILE__))}/core';#{element}.#{method};"
256
- ruby_code
257
- end
258
-
259
- private :generate_ruby_code
260
-
261
- def spawned_no_wait_command(command)
262
- command = "-e #{command.inspect}"
263
- unless $DEBUG
264
- "start rubyw #{command}"
265
- else
266
- puts "#no_wait command:"
267
- command = "ruby #{command}"
268
- puts command
269
- command
270
- end
271
- end
272
-
273
- private :spawned_no_wait_command
274
-
275
- def click!
276
- perform_action do
277
- # Not sure why but in IE9 Document mode, passing a parameter
278
- # to click seems to work. Firing the onClick event breaks other tests
279
- # so this seems to be the safest change and also works fine in IE8
280
- ole_object.click(0)
281
- end
282
- end
283
-
284
- # Flash the element the specified number of times.
285
- # Defaults to 10 flashes.
286
- def flash number=10
177
+ # Flash the element the specified number of times for troubleshooting purposes.
178
+ # @param [Fixnum] number Number times to flash the element.
179
+ # @macro exists
180
+ def flash(number=10)
287
181
  assert_exists
288
182
  number.times do
289
- highlight(:set)
183
+ set_highlight
290
184
  sleep 0.05
291
- highlight(:clear)
185
+ clear_highlight
292
186
  sleep 0.05
293
187
  end
294
- nil
188
+ self
295
189
  end
296
190
 
297
- # Executes a user defined "fireEvent" for objects with JavaScript events tied to them such as DHTML menus.
298
- # usage: allows a generic way to fire javascript events on page objects such as "onMouseOver", "onClick", etc.
299
- # raises: UnknownObjectException if the object is not found
300
- # ObjectDisabledException if the object is currently disabled
191
+ # Executes a user defined "fireEvent" for element with JavaScript events.
192
+ #
193
+ # @example Fire a onchange event on select_list:
194
+ # browser.select_list.fire_event "onchange"
195
+ #
196
+ # @macro exists
301
197
  def fire_event(event)
302
198
  perform_action {dispatch_event(event); @container.wait}
303
199
  end
304
200
 
305
- def dispatch_event(event)
306
- if IE.version_parts.first.to_i >= 9 && container.page_container.document.documentMode.to_i >= 9
307
- ole_object.dispatchEvent(create_event(event))
308
- else
309
- ole_object.fireEvent(event)
310
- end
311
- end
312
-
313
- def create_event(event)
314
- event =~ /on(.*)/i
315
- event = $1 if $1
316
- event.downcase!
317
- # See http://www.howtocreate.co.uk/tutorials/javascript/domevents
318
- case event
319
- when 'abort', 'blur', 'change', 'error', 'focus', 'load',
320
- 'reset', 'resize', 'scroll', 'select', 'submit', 'unload'
321
- event_name = :initEvent
322
- event_type = 'HTMLEvents'
323
- event_args = [event, true, true]
324
- when 'select'
325
- event_name = :initUIEvent
326
- event_type = 'UIEvent'
327
- event_args = [event, true, true, @container.page_container.document.parentWindow.window,0]
328
- when 'keydown', 'keypress', 'keyup'
329
- event_name = :initKeyboardEvent
330
- event_type = 'KeyboardEvent'
331
- # 'type', bubbles, cancelable, windowObject, ctrlKey, altKey, shiftKey, metaKey, keyCode, charCode
332
- event_args = [event, true, true, @container.page_container.document.parentWindow.window, false, false, false, false, 0, 0]
333
- when 'click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup',
334
- 'contextmenu', 'drag', 'dragstart', 'dragenter', 'dragover', 'dragleave', 'dragend', 'drop', 'selectstart'
335
- event_name = :initMouseEvent
336
- event_type = 'MouseEvents'
337
- # 'type', bubbles, cancelable, windowObject, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget
338
- 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]
339
- else
340
- raise UnhandledEventException, "Don't know how to trigger event '#{event}'"
341
- end
342
- event = @container.page_container.document.createEvent(event_type)
343
- event.send event_name, *event_args
344
- event
345
- end
346
-
347
- # This method sets focus on the active element.
348
- # raises: UnknownObjectException if the object is not found
349
- # ObjectDisabledException if the object is currently disabled
201
+ # Set focus on the element.
202
+ # @macro exists
203
+ # @macro enabled
350
204
  def focus
351
205
  assert_exists
352
206
  assert_enabled
@@ -354,13 +208,16 @@ module Watir
354
208
  ole_object.focus(0)
355
209
  end
356
210
 
211
+ # @return [Boolean] true when element is in focus, false otherwise.
212
+ # @macro exists
213
+ # @macro enabled
357
214
  def focused?
358
215
  assert_exists
359
216
  assert_enabled
360
217
  @page_container.document.activeElement.uniqueNumber == unique_number
361
218
  end
362
219
 
363
- # Returns whether this element actually exists.
220
+ # @return [Boolean] true when element exists, false otherwise.
364
221
  def exists?
365
222
  begin
366
223
  locate
@@ -372,22 +229,24 @@ module Watir
372
229
 
373
230
  alias :exist? :exists?
374
231
 
375
- # Returns true if the element is enabled, false if it isn't.
376
- # raises: UnknownObjectException if the object is not found
232
+ # @return [Boolean] true if the element is enabled, false otherwise.
233
+ # @macro exists
377
234
  def enabled?
378
235
  assert_exists
379
236
  !disabled?
380
237
  end
381
238
 
239
+ # @return [Boolean] true if the element is disabled, false otherwise.
240
+ # @macro exists
382
241
  def disabled?
383
242
  assert_exists
384
243
  false
385
244
  end
386
245
 
387
- # If any parent element isn't visible then we cannot write to the
388
- # element. The only realiable way to determine this is to iterate
389
- # up the DOM element tree checking every element to make sure it's
390
- # visible.
246
+ # Retrieve the status of element's visibility.
247
+ # When any parent element is not also visible then the current element is determined as not visible too.
248
+ # @return [Boolean] true if element is visible, false otherwise.
249
+ # @macro exists
391
250
  def visible?
392
251
  # Now iterate up the DOM element tree and return false if any
393
252
  # parent element isn't visible
@@ -409,22 +268,22 @@ module Watir
409
268
  end
410
269
 
411
270
  # Get attribute value for any attribute of the element.
412
- # Returns null if attribute doesn't exist.
271
+ # @return [String] the value of the attribute.
272
+ # @return [Object] nil if the attribute does not exist.
273
+ # @macro exists
413
274
  def attribute_value(attribute_name)
414
275
  assert_exists
415
276
  ole_object.getAttribute(attribute_name)
416
277
  end
417
278
 
418
- def perform_action
419
- assert_exists
420
- assert_enabled
421
- highlight(:set)
422
- yield
423
- highlight(:clear)
424
- end
425
-
426
- private :perform_action
427
-
279
+ # Make it possible to use *_no_wait commands and retrieve element html5 data-attribute
280
+ # values.
281
+ #
282
+ # @example Use click without waiting:
283
+ # browser.button.click_no_wait
284
+ #
285
+ # @example Retrieve html5 data attribute value:
286
+ # browser.div.data_model # => value of data-model="foo" html attribute
428
287
  def method_missing(method_name, *args, &block)
429
288
  meth = method_name.to_s
430
289
  if meth =~ /(.*)_no_wait/ && self.respond_to?($1)
@@ -438,6 +297,182 @@ module Watir
438
297
  super
439
298
  end
440
299
  end
441
-
300
+
301
+ # @private
302
+ def locate
303
+ @o = @container.locator_for(TaggedElementLocator, @specifiers, self.class).locate
304
+ end
305
+
306
+ # @private
307
+ def __ole_inner_elements
308
+ assert_exists
309
+ ole_object.all
310
+ end
311
+
312
+ # @private
313
+ def document
314
+ assert_exists
315
+ ole_object
316
+ end
317
+
318
+ # @private
319
+ def assert_exists
320
+ locate
321
+ unless ole_object
322
+ exception_class = self.is_a?(Frame) ? UnknownFrameException : UnknownObjectException
323
+ raise exception_class.new(Watir::Exception.message_for_unable_to_locate(@specifiers))
324
+ end
325
+ end
326
+
327
+ # @private
328
+ def assert_enabled
329
+ raise ObjectDisabledException, "object #{@specifiers.inspect} is disabled" unless enabled?
330
+ end
331
+
332
+ # @private
333
+ def typingspeed
334
+ @container.typingspeed
335
+ end
336
+
337
+ # @private
338
+ def type_keys
339
+ @type_keys || @container.type_keys
340
+ end
341
+
342
+ # @private
343
+ def active_object_highlight_color
344
+ @container.active_object_highlight_color
345
+ end
346
+
347
+ # @private
348
+ def click!
349
+ perform_action do
350
+ # Not sure why but in IE9 Document mode, passing a parameter
351
+ # to click seems to work. Firing the onClick event breaks other tests
352
+ # so this seems to be the safest change and also works fine in IE8
353
+ ole_object.click(0)
354
+ end
355
+ end
356
+
357
+ # @private
358
+ def dispatch_event(event)
359
+ if IE.version_parts.first.to_i >= 9 && container.page_container.document.documentMode.to_i >= 9
360
+ ole_object.dispatchEvent(create_event(event))
361
+ else
362
+ ole_object.fireEvent(event)
363
+ end
364
+ end
365
+
366
+ private
367
+
368
+ def create_event(event)
369
+ event =~ /on(.*)/i
370
+ event = $1 if $1
371
+ event.downcase!
372
+ # See http://www.howtocreate.co.uk/tutorials/javascript/domevents
373
+ case event
374
+ when 'abort', 'blur', 'change', 'error', 'focus', 'load',
375
+ 'reset', 'resize', 'scroll', 'select', 'submit', 'unload'
376
+ event_name = :initEvent
377
+ event_type = 'HTMLEvents'
378
+ event_args = [event, true, true]
379
+ when 'select'
380
+ event_name = :initUIEvent
381
+ event_type = 'UIEvent'
382
+ event_args = [event, true, true, @container.page_container.document.parentWindow.window,0]
383
+ when 'keydown', 'keypress', 'keyup'
384
+ event_name = :initKeyboardEvent
385
+ event_type = 'KeyboardEvent'
386
+ # 'type', bubbles, cancelable, windowObject, ctrlKey, altKey, shiftKey, metaKey, keyCode, charCode
387
+ event_args = [event, true, true, @container.page_container.document.parentWindow.window, false, false, false, false, 0, 0]
388
+ when 'click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup',
389
+ 'contextmenu', 'drag', 'dragstart', 'dragenter', 'dragover', 'dragleave', 'dragend', 'drop', 'selectstart'
390
+ event_name = :initMouseEvent
391
+ event_type = 'MouseEvents'
392
+ # 'type', bubbles, cancelable, windowObject, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget
393
+ 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]
394
+ else
395
+ raise UnhandledEventException, "Don't know how to trigger event '#{event}'"
396
+ end
397
+ event = @container.page_container.document.createEvent(event_type)
398
+ event.send event_name, *event_args
399
+ event
400
+ end
401
+
402
+ # Return an array with many of the properties, in a format to be used by the to_s method
403
+ def string_creator
404
+ n = []
405
+ n << "id:".ljust(TO_S_SIZE) + self.id.to_s
406
+ n
407
+ end
408
+
409
+ # This method is responsible for setting colored highlighting on the currently active element.
410
+ def set_highlight
411
+ perform_highlight do
412
+ @original_color = ole_object.style.backgroundColor
413
+ ole_object.style.backgroundColor = @container.active_object_highlight_color
414
+ end
415
+ end
416
+
417
+ # This method is responsible for clearing colored highlighting on the currently active element.
418
+ def clear_highlight
419
+ perform_highlight do
420
+ ole_object.style.backgroundColor = @original_color if @original_color
421
+ end
422
+ end
423
+
424
+ def perform_highlight
425
+ yield
426
+ rescue
427
+ # we could be here for a number of reasons...
428
+ # e.g. page may have reloaded and the reference is no longer valid
429
+ end
430
+
431
+ def replace_method(method)
432
+ method == 'click' ? 'click!' : method
433
+ end
434
+
435
+ def build_method(method_name, *args)
436
+ arguments = args.map do |argument|
437
+ if argument.is_a?(String)
438
+ argument = "'#{argument}'"
439
+ else
440
+ argument = argument.inspect
441
+ end
442
+ end
443
+ "#{replace_method(method_name)}(#{arguments.join(',')})"
444
+ end
445
+
446
+ def generate_ruby_code(element, method_name, *args)
447
+ # needs to be done like this to avoid segfault on ruby 1.9.3
448
+ tag_name = @specifiers[:tag_name].join("' << '")
449
+ element = "#{self.class}.new(#{@page_container.attach_command}, :tag_name => Array.new << '#{tag_name}', :unique_number => #{unique_number})"
450
+ method = build_method(method_name, *args)
451
+ ruby_code = "$:.unshift(#{$LOAD_PATH.map {|p| "'#{p}'" }.join(").unshift(")});" <<
452
+ "require '#{File.expand_path(File.dirname(__FILE__))}/core';#{element}.#{method};"
453
+ ruby_code
454
+ end
455
+
456
+ def spawned_no_wait_command(command)
457
+ command = "-e #{command.inspect}"
458
+ unless $DEBUG
459
+ "start rubyw #{command}"
460
+ else
461
+ puts "#no_wait command:"
462
+ command = "ruby #{command}"
463
+ puts command
464
+ command
465
+ end
466
+ end
467
+
468
+ def perform_action
469
+ assert_exists
470
+ assert_enabled
471
+ set_highlight
472
+ yield
473
+ ensure
474
+ clear_highlight
475
+ end
476
+
442
477
  end
443
478
  end