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.
- data/CHANGES +17 -0
- data/Gemfile.lock +9 -8
- data/LICENSE +1 -0
- data/README.rdoc +6 -7
- data/Rakefile +3 -1
- data/VERSION +1 -1
- data/lib/watir-classic.rb +0 -5
- data/lib/watir-classic/browser.rb +58 -35
- data/lib/watir-classic/browsers.rb +1 -1
- data/lib/watir-classic/container.rb +39 -33
- data/lib/watir-classic/cookies.rb +32 -2
- data/lib/watir-classic/core.rb +0 -1
- data/lib/watir-classic/dialogs/alert.rb +12 -0
- data/lib/watir-classic/dialogs/file_field.rb +11 -0
- data/lib/watir-classic/drag_and_drop_helper.rb +14 -0
- data/lib/watir-classic/element.rb +292 -257
- data/lib/watir-classic/element_collection.rb +26 -8
- data/lib/watir-classic/element_extensions.rb +22 -16
- data/lib/watir-classic/exceptions.rb +4 -4
- data/lib/watir-classic/form.rb +52 -49
- data/lib/watir-classic/frame.rb +23 -14
- data/lib/watir-classic/ie-class.rb +363 -315
- data/lib/watir-classic/ie-process.rb +1 -0
- data/lib/watir-classic/ie.rb +0 -17
- data/lib/watir-classic/image.rb +58 -64
- data/lib/watir-classic/input_elements.rb +224 -219
- data/lib/watir-classic/link.rb +14 -15
- data/lib/watir-classic/locator.rb +12 -7
- data/lib/watir-classic/matches.rb +7 -3
- data/lib/watir-classic/modal_dialog.rb +38 -26
- data/lib/watir-classic/non_control_elements.rb +29 -0
- data/lib/watir-classic/options.rb +10 -15
- data/lib/watir-classic/page-container.rb +30 -48
- data/lib/watir-classic/process.rb +4 -2
- data/lib/watir-classic/screenshot.rb +6 -0
- data/lib/watir-classic/supported_elements.rb +36 -14
- data/lib/watir-classic/table.rb +81 -71
- data/lib/watir-classic/util.rb +9 -11
- data/lib/watir-classic/wait.rb +17 -4
- data/lib/watir-classic/wait_helper.rb +15 -2
- data/lib/watir-classic/win32.rb +2 -1
- data/lib/watir-classic/window.rb +35 -7
- data/lib/watir-classic/xpath_locator.rb +1 -0
- data/lib/watir-classic/yard/global_macros.rb +7 -0
- data/spec/frame_spec.rb +17 -0
- metadata +5 -7
- data/lib/watir-classic/close_all.rb +0 -31
- data/lib/watir-classic/contrib/enabled_popup.rb +0 -21
- data/lib/watir-classic/contrib/ie-new-process.rb +0 -27
- data/lib/watir-classic/contrib/page_checker.rb +0 -29
- 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
|
-
|
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
|
-
|
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
|
data/lib/watir-classic/core.rb
CHANGED
@@ -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
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
50
|
-
|
51
|
-
|
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
|
75
|
-
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
|
122
|
-
|
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
|
-
#
|
129
|
-
#
|
130
|
-
#
|
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
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
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
|
-
|
155
|
-
|
156
|
-
|
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
|
-
|
231
|
-
|
232
|
-
|
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
|
-
|
183
|
+
set_highlight
|
290
184
|
sleep 0.05
|
291
|
-
|
185
|
+
clear_highlight
|
292
186
|
sleep 0.05
|
293
187
|
end
|
294
|
-
|
188
|
+
self
|
295
189
|
end
|
296
190
|
|
297
|
-
# Executes a user defined "fireEvent" for
|
298
|
-
#
|
299
|
-
#
|
300
|
-
#
|
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
|
-
|
306
|
-
|
307
|
-
|
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
|
-
#
|
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
|
-
#
|
376
|
-
#
|
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
|
-
#
|
388
|
-
# element
|
389
|
-
#
|
390
|
-
#
|
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
|
-
#
|
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
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
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
|