watir-classic 3.3.0 → 3.4.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.
- 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
|