vapir-ie 1.7.0.rc1
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/History.txt +437 -0
- data/LICENSE.txt +41 -0
- data/README.txt +0 -0
- data/lib/vapir-ie.rb +44 -0
- data/lib/vapir-ie/AutoItX.chm +0 -0
- data/lib/vapir-ie/AutoItX3.dll +0 -0
- data/lib/vapir-ie/IEDialog/Release/IEDialog.dll +0 -0
- data/lib/vapir-ie/autoit.rb +13 -0
- data/lib/vapir-ie/close_all.rb +32 -0
- data/lib/vapir-ie/container.rb +51 -0
- data/lib/vapir-ie/element.rb +376 -0
- data/lib/vapir-ie/elements.rb +9 -0
- data/lib/vapir-ie/form.rb +8 -0
- data/lib/vapir-ie/frame.rb +24 -0
- data/lib/vapir-ie/ie-class.rb +880 -0
- data/lib/vapir-ie/ie-process.rb +60 -0
- data/lib/vapir-ie/image.rb +59 -0
- data/lib/vapir-ie/input_elements.rb +158 -0
- data/lib/vapir-ie/link.rb +23 -0
- data/lib/vapir-ie/logger.rb +21 -0
- data/lib/vapir-ie/modal_dialog.rb +224 -0
- data/lib/vapir-ie/non_control_elements.rb +77 -0
- data/lib/vapir-ie/page_container.rb +203 -0
- data/lib/vapir-ie/process.rb +22 -0
- data/lib/vapir-ie/screen_capture.rb +7 -0
- data/lib/vapir-ie/scripts/select_file.rb +79 -0
- data/lib/vapir-ie/table.rb +33 -0
- data/lib/vapir-ie/version.rb +5 -0
- data/lib/vapir-ie/win32ole.rb +45 -0
- data/lib/vapir-ie/win32ole/win32ole.so +0 -0
- data/lib/vapir/ie.rb +1 -0
- metadata +130 -0
data/README.txt
ADDED
File without changes
|
data/lib/vapir-ie.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'vapir-common'
|
2
|
+
|
3
|
+
# create stub class since everything is defined in Vapir::IE namespace - this needs to be defined before the real class.
|
4
|
+
require 'vapir-common/browser'
|
5
|
+
module Vapir
|
6
|
+
IE= Class.new(Vapir::Browser)
|
7
|
+
end
|
8
|
+
|
9
|
+
# these switches need to be deleted from ARGV to enable the Test::Unit
|
10
|
+
# functionality that grabs
|
11
|
+
# the remaining ARGV as a filter on what tests to run.
|
12
|
+
# Note: this means that watir must be require'd BEFORE test/unit.
|
13
|
+
# (Alternatively, you could require test/unit first and then put the Vapir::IE
|
14
|
+
# arguments after the '--'.)
|
15
|
+
|
16
|
+
# Make Internet Explorer invisible. -b stands for background
|
17
|
+
$HIDE_IE ||= ARGV.delete('-b')
|
18
|
+
|
19
|
+
# Run fast
|
20
|
+
$FAST_SPEED = ARGV.delete('-f')
|
21
|
+
|
22
|
+
# Eat the -s command line switch (deprecated)
|
23
|
+
ARGV.delete('-s')
|
24
|
+
|
25
|
+
require 'vapir-ie/ie-class'
|
26
|
+
require 'vapir-ie/elements'
|
27
|
+
require 'vapir-ie/version'
|
28
|
+
|
29
|
+
require 'vapir-common/waiter'
|
30
|
+
|
31
|
+
module Vapir
|
32
|
+
include Vapir::Exception
|
33
|
+
|
34
|
+
# Directory containing the watir.rb file
|
35
|
+
@@dir = File.expand_path(File.dirname(__FILE__))
|
36
|
+
|
37
|
+
ATTACHER = Waiter.new
|
38
|
+
# Like regular Ruby "until", except that a TimeOutException is raised
|
39
|
+
# if the timeout is exceeded. Timeout is IE.attach_timeout.
|
40
|
+
def self.until_with_timeout # block
|
41
|
+
ATTACHER.timeout = IE.attach_timeout
|
42
|
+
ATTACHER.wait_until { yield }
|
43
|
+
end
|
44
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Vapir
|
2
|
+
AutoItDLL=File.join(File.expand_path(File.dirname(__FILE__)),'AutoItX3.dll')
|
3
|
+
def self.autoit
|
4
|
+
@@autoit||= begin
|
5
|
+
begin
|
6
|
+
WIN32OLE.new('AutoItX3.Control')
|
7
|
+
rescue WIN32OLERuntimeError
|
8
|
+
system("regsvr32.exe /s \"#{AutoItDLL.gsub('/', '\\')}\"")
|
9
|
+
WIN32OLE.new('AutoItX3.Control')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'vapir-ie/ie-class'
|
2
|
+
|
3
|
+
module Vapir
|
4
|
+
class IE
|
5
|
+
# close all ie browser windows
|
6
|
+
def self.close_all
|
7
|
+
close_all_but nil
|
8
|
+
end
|
9
|
+
# find other ie browser windows and close them
|
10
|
+
def close_others
|
11
|
+
IE.close_all_but self
|
12
|
+
end
|
13
|
+
private
|
14
|
+
def self.close_all_but(except=nil)
|
15
|
+
Vapir::IE.each do |ie|
|
16
|
+
ie.close_modal
|
17
|
+
ie.close unless except and except.hwnd == ie.hwnd
|
18
|
+
end
|
19
|
+
sleep 1.0 # replace with polling for window count to be zero?
|
20
|
+
end
|
21
|
+
public
|
22
|
+
# close modal dialog. unlike IE#modal_dialog.close, does not wait for dialog
|
23
|
+
# to appear and does not raise exception if no window is found.
|
24
|
+
# returns true if modal was found and close, otherwise false
|
25
|
+
def close_modal
|
26
|
+
modal_dialog=modal_dialog(:timeout => 0, :error => false)
|
27
|
+
if modal_dialog && modal_dialog.exists?
|
28
|
+
modal_dialog.close
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'vapir-common/container'
|
2
|
+
|
3
|
+
module Vapir
|
4
|
+
# This module contains the factory methods that are used to access most html objects
|
5
|
+
#
|
6
|
+
# For example, to access a button on a web page that has the following html
|
7
|
+
# <input type = button name= 'b1' value='Click Me' onClick='javascript:doSomething()'>
|
8
|
+
#
|
9
|
+
# the following watir code could be used
|
10
|
+
#
|
11
|
+
# ie.button(:name, 'b1').click
|
12
|
+
#
|
13
|
+
# or
|
14
|
+
#
|
15
|
+
# ie.button(:value, 'Click Me').to_s
|
16
|
+
#
|
17
|
+
# there are many methods available to the Button object
|
18
|
+
#
|
19
|
+
# Is includable for classes that have @container, document and ole_inner_elements
|
20
|
+
module IE::Container
|
21
|
+
include Vapir::Container
|
22
|
+
include Vapir::Exception
|
23
|
+
|
24
|
+
# Note: @container is the container of this object, i.e. the container
|
25
|
+
# of this container.
|
26
|
+
# In other words, for ie.table().this_thing().text_field().set,
|
27
|
+
# container of this_thing is the table.
|
28
|
+
|
29
|
+
# This is used to change the typing speed when entering text on a page.
|
30
|
+
attr_accessor :typingspeed
|
31
|
+
attr_accessor :type_keys
|
32
|
+
|
33
|
+
def copy_test_config(container) # only used by form and frame
|
34
|
+
@typingspeed = container.typingspeed
|
35
|
+
@type_keys = container.type_keys
|
36
|
+
end
|
37
|
+
private :copy_test_config
|
38
|
+
|
39
|
+
# Write the specified string to the log.
|
40
|
+
def log(what)
|
41
|
+
@container.logger.debug(what) if @logger
|
42
|
+
end
|
43
|
+
|
44
|
+
def set_container container
|
45
|
+
@container = container
|
46
|
+
@page_container = container.page_container
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
end # module
|
51
|
+
end
|
@@ -0,0 +1,376 @@
|
|
1
|
+
require 'vapir-common/element'
|
2
|
+
require 'vapir-ie/container'
|
3
|
+
|
4
|
+
module Vapir
|
5
|
+
# Base class for html elements.
|
6
|
+
# This is not a class that users would normally access.
|
7
|
+
class IE::Element # Wrapper
|
8
|
+
include IE::Container # presumes @container is defined
|
9
|
+
include Vapir::Element
|
10
|
+
include Vapir::Exception
|
11
|
+
|
12
|
+
alias containing_object element_object
|
13
|
+
alias ole_object element_object # TODO: deprecate this?
|
14
|
+
|
15
|
+
dom_attr :currentStyle => [:current_style_object, :computed_style_object]
|
16
|
+
alias_deprecated :currentStyle, :current_style_object
|
17
|
+
dom_attr :disabled => [:disabled, :disabled?] # this applies to all elements for IE, apparently.
|
18
|
+
def enabled?
|
19
|
+
!disabled
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def base_element_class
|
24
|
+
IE::Element
|
25
|
+
end
|
26
|
+
def browser_class
|
27
|
+
IE
|
28
|
+
end
|
29
|
+
|
30
|
+
public
|
31
|
+
|
32
|
+
# return the unique COM number for the element
|
33
|
+
dom_attr :uniqueNumber => :unique_number
|
34
|
+
# Return the outer html of the object - see http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/outerhtml.asp?frame=true
|
35
|
+
dom_attr :outerHTML => :outer_html
|
36
|
+
|
37
|
+
# return the text before the element
|
38
|
+
def before_text
|
39
|
+
element_object.getAdjacentText("afterEnd")
|
40
|
+
end
|
41
|
+
|
42
|
+
# return the text after the element
|
43
|
+
def after_text
|
44
|
+
element_object.getAdjacentText("beforeBegin")
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns the text content of the element.
|
48
|
+
dom_attr :innerText => :text
|
49
|
+
|
50
|
+
# include Comparable
|
51
|
+
# def <=> other
|
52
|
+
# assert_exists
|
53
|
+
# other.assert_exists
|
54
|
+
# ole_object.sourceindex <=> other.ole_object.sourceindex
|
55
|
+
# end
|
56
|
+
dom_attr :sourceIndex => :source_index
|
57
|
+
|
58
|
+
# Return true if self is contained earlier in the html than other.
|
59
|
+
def before?(other)
|
60
|
+
source_index < other.source_index
|
61
|
+
end
|
62
|
+
# Return true if self is contained later in the html than other.
|
63
|
+
def after?(other)
|
64
|
+
source_index > other.source_index
|
65
|
+
end
|
66
|
+
|
67
|
+
def typingspeed
|
68
|
+
@container.typingspeed
|
69
|
+
end
|
70
|
+
def type_keys
|
71
|
+
@type_keys || @container.type_keys
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
# for use with events' button property
|
76
|
+
MouseButtonCodes=
|
77
|
+
{ :left => 1,
|
78
|
+
:middle => 4,
|
79
|
+
:right => 2,
|
80
|
+
}
|
81
|
+
|
82
|
+
# returns an object representing an event (a WIN32OLE object)
|
83
|
+
# see:
|
84
|
+
# http://msdn.microsoft.com/en-us/library/ms535863%28VS.85%29.aspx
|
85
|
+
def create_event_object(event_type, options)
|
86
|
+
event_object=document_object.createEventObject
|
87
|
+
|
88
|
+
event_object_hash=create_event_object_hash(event_type, options)
|
89
|
+
event_object_hash.each_pair do |key,val|
|
90
|
+
event_object.send(key.to_s+'=', val)
|
91
|
+
end
|
92
|
+
return event_object
|
93
|
+
end
|
94
|
+
|
95
|
+
def create_event_object_hash(event_type, options)
|
96
|
+
event_stuff=
|
97
|
+
{ :type => event_type,
|
98
|
+
:keyCode => 0, # TODO/fix, implement this
|
99
|
+
:ctrlKey => false,
|
100
|
+
:ctrlLeft => false,
|
101
|
+
:altKey => false,
|
102
|
+
:altLeft => false,
|
103
|
+
:shiftKey => false,
|
104
|
+
:shiftLeft => false,
|
105
|
+
}
|
106
|
+
|
107
|
+
if %w(onclick onmousedown onmouseup ondblclick onmouseover onmouseout).include?(event_type)
|
108
|
+
client_center=self.client_center
|
109
|
+
|
110
|
+
button_code=options[:button_code] ||
|
111
|
+
MouseButtonCodes[options[:button]] ||
|
112
|
+
(%w(onclick onmousedown onmouseup ondblclick).include?(event_type) ? MouseButtonCodes[:left] : 0)
|
113
|
+
|
114
|
+
event_stuff.merge!(
|
115
|
+
{ :screenX => 0, # TODO/fix: use screen_center when implemented
|
116
|
+
:screenY => 0,
|
117
|
+
:clientX => client_center[0],
|
118
|
+
:clientY => client_center[1],
|
119
|
+
#:offsetX => , # if set this will clobber clientX/clientY. is itself set from clientX/Y when not set.
|
120
|
+
#:offsetY => ,
|
121
|
+
#:x => , # these also seem to get set from clientX/Y
|
122
|
+
#:y => ,
|
123
|
+
:button => button_code,
|
124
|
+
})
|
125
|
+
end
|
126
|
+
relevant_options=options.reject do |optkey,optv|
|
127
|
+
!%w(type keyCode ctrlKey ctrlLeft altKey altLeft shiftKey shiftLeft screenX screenY clientX clientY offsetX offsetY x y).detect{|keystr| keystr.to_sym==optkey}
|
128
|
+
end
|
129
|
+
return event_stuff.merge(relevant_options)
|
130
|
+
end
|
131
|
+
|
132
|
+
# makes json for an event object from the given options.
|
133
|
+
# does so in a sort of naive way, but a way that doesn't require
|
134
|
+
# something as heavyweight as pulling in ActiveSupport or the JSON gem.
|
135
|
+
def create_event_object_json(options)
|
136
|
+
event_object_hash=create_event_object_hash(nil, options).reject{|(k,v)| v.nil? }
|
137
|
+
event_object_json="{"+event_object_hash.map do |(attr, val)|
|
138
|
+
raise RuntimeError, "unexpected attribute #{attr}" unless attr.is_a?(Symbol) && attr.to_s=~ /\A[\w_]+\z/
|
139
|
+
unless [Numeric, String, TrueClass, FalseClass].any?{|klass| val.is_a?(klass) }
|
140
|
+
raise ArgumentError, "Cannot pass given key/value pair: #{attr.inspect} => #{val.inspect} (#{val.class})"
|
141
|
+
end
|
142
|
+
attr.to_s.inspect+": "+val.inspect
|
143
|
+
end.join(", ")+"}"
|
144
|
+
end
|
145
|
+
|
146
|
+
public
|
147
|
+
|
148
|
+
# Fires the click event on this element.
|
149
|
+
#
|
150
|
+
# Options:
|
151
|
+
# - :wait => true or false. If true, waits for the javascript call to return, and calls the #wait method.
|
152
|
+
# If false, does not wait for the javascript to return and does not call #wait.
|
153
|
+
# Default is true.
|
154
|
+
# - :highlight => true or false. Highlights the element while clicking if true. Default is true.
|
155
|
+
def click(options={})
|
156
|
+
options={:wait => true, :highlight => true}.merge(options)
|
157
|
+
result=nil
|
158
|
+
with_highlight(options) do
|
159
|
+
assert_enabled if respond_to?(:assert_enabled)
|
160
|
+
if options[:wait]
|
161
|
+
# we're putting all of the clicking actions in an array so that we can more easily separate out the
|
162
|
+
# overhead of checking existence and returning if existence fails.
|
163
|
+
actions=
|
164
|
+
[ proc { fire_event('mousedown', options) },
|
165
|
+
proc { fire_event('mouseup', options) },
|
166
|
+
#proc { fire_event('click', options) },
|
167
|
+
proc { element_object.respond_to?(:click) ? element_object.click : fire_event('click', options)
|
168
|
+
# TODO/fix: this calls the 'click' function if there is one, but that doesn't pass information
|
169
|
+
# like button/clientX/etc. figure out how to pass that to the event that click fires.
|
170
|
+
# we can't just use the fire_event, because the click function does more than that. for example,
|
171
|
+
# a link won't be followed just from firing the onclick event; the click function has to be called.
|
172
|
+
},
|
173
|
+
]
|
174
|
+
actions.each do |action|
|
175
|
+
# javascript stuff responding to previous events can cause self to stop existing, so check at every subsequent step
|
176
|
+
if exists?
|
177
|
+
result=action.call
|
178
|
+
else
|
179
|
+
return result
|
180
|
+
end
|
181
|
+
end
|
182
|
+
wait
|
183
|
+
result
|
184
|
+
else
|
185
|
+
document_object.parentWindow.setTimeout("
|
186
|
+
(function(tagName, uniqueNumber, event_options)
|
187
|
+
{ var event_object=document.createEventObject();
|
188
|
+
for(key in event_options)
|
189
|
+
{ event_object[key]=event_options[key];
|
190
|
+
}
|
191
|
+
var candidate_elements=document.getElementsByTagName(tagName);
|
192
|
+
for(var i=0;i<candidate_elements.length;++i)
|
193
|
+
{ var element=candidate_elements[i];
|
194
|
+
if(element.uniqueNumber==uniqueNumber)
|
195
|
+
{ element.fireEvent('onmousedown', event_object);
|
196
|
+
element.fireEvent('onmouseup', event_object);
|
197
|
+
//element.fireEvent('onclick', event_object); // #TODO/fix - same as above with click() vs fireEvent('onclick', ...)
|
198
|
+
element.click ? element.click() : element.fireEvent('onclick', event_object);
|
199
|
+
}
|
200
|
+
}
|
201
|
+
})(#{self.tagName.inspect}, #{element_object.uniqueNumber.inspect}, #{create_event_object_json(options)})
|
202
|
+
", 0)
|
203
|
+
nil
|
204
|
+
end
|
205
|
+
end
|
206
|
+
result
|
207
|
+
end
|
208
|
+
|
209
|
+
# calls #click with :wait option false.
|
210
|
+
# Takes options:
|
211
|
+
# - :highlight => true or false. Highlights the element while clicking if true. Default is true.
|
212
|
+
def click_no_wait(options={})
|
213
|
+
click(options.merge(:wait => false))
|
214
|
+
end
|
215
|
+
|
216
|
+
# Executes a user defined "fireEvent" for objects with JavaScript events tied to them such as DHTML menus.
|
217
|
+
# usage: allows a generic way to fire javascript events on page objects such as "onMouseOver", "onClick", etc.
|
218
|
+
# raises: UnknownObjectException if the object is not found
|
219
|
+
# ObjectDisabledException if the object is currently disabled
|
220
|
+
def fire_event(event_type, options={})
|
221
|
+
event_type = event_type.to_s.downcase # in case event_type was given as a symbol
|
222
|
+
unless event_type =~ /\Aon(.*)\z/i
|
223
|
+
event_type = "on"+event_type
|
224
|
+
end
|
225
|
+
|
226
|
+
options={:highlight => true, :wait => true}.merge(options)
|
227
|
+
with_highlight(options) do
|
228
|
+
assert_enabled if respond_to?(:assert_enabled)
|
229
|
+
if options[:wait]
|
230
|
+
# we need to pass fireEvent two arguments - the event type, and the event object.
|
231
|
+
# we can't do this directly. there is a bug or something, the result of which is
|
232
|
+
# that if we pass the WIN32OLE that is the return from document.createEventObject,
|
233
|
+
# none of the information about it actually gets passed. its button attribute is
|
234
|
+
# 0 regardless of what it was set to; same with clientx, clientY, and everything else.
|
235
|
+
# this seems to be an issue only with passing arguments which are WIN32OLEs to
|
236
|
+
# functions that are native functions (as opposed to functions defined by a user
|
237
|
+
# in javascript). so, a workaround is to make a function that is written in javascript
|
238
|
+
# that wraps around the native function and just passes the arguments to it. this
|
239
|
+
# causes the objects to be passed correctly. to illustrate, compare:
|
240
|
+
# window.alert(document.createEventObject)
|
241
|
+
# this causes an alert to appear with the text "[object]"
|
242
|
+
# window.eval("alert_wrapped=function(message){alert(message);}")
|
243
|
+
# window.alert_wrapped(document.createEventObject)
|
244
|
+
# this causes an alert to appear with the text "[object Event]"
|
245
|
+
# so, information is lost in the first one, where it's passed straight
|
246
|
+
# to the native function but not in the second one where the native function
|
247
|
+
# is wrapped in a javascript function.
|
248
|
+
#
|
249
|
+
# a generic solution follows, but it doesn't work. I'm leaving it in here in case
|
250
|
+
# I can figure out something to do with it later:
|
251
|
+
#window.eval("watir_wrap_native_for_win32ole=function(object, functionname)
|
252
|
+
# { var args=[];
|
253
|
+
# for(var i=2; i<arguments.length; ++i)
|
254
|
+
# { args.push(arguments[i]);
|
255
|
+
# }
|
256
|
+
# return object[functionname].apply(object, args);
|
257
|
+
# }")
|
258
|
+
#
|
259
|
+
# the problem with the above, using apply, is that it sometimse raises:
|
260
|
+
# WIN32OLERuntimeError: watir_wrap_native_for_win32ole
|
261
|
+
# OLE error code:0 in <Unknown>
|
262
|
+
# <No Description>
|
263
|
+
# HRESULT error code:0x80020101
|
264
|
+
#
|
265
|
+
# so, instead, implementing to a version that doesn't use apply but
|
266
|
+
# therefore has to have fixed number of arguments.
|
267
|
+
# TODO: move this to its own function? will when I run into a need for it outside of here, I guess.
|
268
|
+
window=document_object.parentWindow
|
269
|
+
window.eval("watir_wrap_native_for_win32ole_two_args=function(object, functionname, arg1, arg2)
|
270
|
+
{ return object[functionname](arg1, arg2);
|
271
|
+
}")
|
272
|
+
# then use it, passing the event object.
|
273
|
+
# thus the buttons and mouse position and all that are successfully passed.
|
274
|
+
event_object= create_event_object(event_type, options)
|
275
|
+
result=window.watir_wrap_native_for_win32ole_two_args(element_object, 'fireEvent', event_type, event_object)
|
276
|
+
wait
|
277
|
+
result
|
278
|
+
else
|
279
|
+
document_object.parentWindow.setTimeout("
|
280
|
+
(function(tagName, uniqueNumber, event_type, event_options)
|
281
|
+
{ var event_object=document.createEventObject();
|
282
|
+
for(key in event_options)
|
283
|
+
{ event_object[key]=event_options[key];
|
284
|
+
}
|
285
|
+
var candidate_elements=document.getElementsByTagName(tagName);
|
286
|
+
for(var i=0;i<candidate_elements.length;++i)
|
287
|
+
{ if(candidate_elements[i].uniqueNumber==uniqueNumber)
|
288
|
+
{ candidate_elements[i].fireEvent(event_type, event_object);
|
289
|
+
}
|
290
|
+
}
|
291
|
+
})(#{self.tagName.inspect}, #{element_object.uniqueNumber.inspect}, #{event_type.to_s.inspect}, #{create_event_object_json(options)})
|
292
|
+
", 0)
|
293
|
+
nil
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
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
|
301
|
+
def fire_event_no_wait(event, options={})
|
302
|
+
fire_event(event, options.merge(:wait => false))
|
303
|
+
end
|
304
|
+
|
305
|
+
def wait(options={})
|
306
|
+
@container.wait(options)
|
307
|
+
end
|
308
|
+
|
309
|
+
def self.element_object_style(element_object, document_object)
|
310
|
+
if element_object.nodeType==1
|
311
|
+
element_object.currentStyle
|
312
|
+
else
|
313
|
+
nil
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
|
318
|
+
# returns a two-element Vector containing the current scroll offset of this element relative
|
319
|
+
# to any scrolling parents.
|
320
|
+
# this is basically stolen from prototype - see http://www.prototypejs.org/api/element/cumulativescrolloffset
|
321
|
+
def scroll_offset
|
322
|
+
# override the #scroll_offset defined in vapir-common because it calls #respond_to? which is very slow on WIN32OLE
|
323
|
+
xy=Vector[0,0]
|
324
|
+
el=element_object
|
325
|
+
begin
|
326
|
+
begin
|
327
|
+
if (scroll_left=el.scrollLeft).is_a?(Numeric) && (scroll_top=el.scrollTop).is_a?(Numeric)
|
328
|
+
xy+=Vector[scroll_left, scroll_top]
|
329
|
+
end
|
330
|
+
rescue WIN32OLERuntimeError
|
331
|
+
# doesn't respond to those; do nothing.
|
332
|
+
end
|
333
|
+
el=el.parentNode
|
334
|
+
end while el
|
335
|
+
xy
|
336
|
+
end
|
337
|
+
|
338
|
+
private
|
339
|
+
def element_object_exists?
|
340
|
+
return nil if !@element_object
|
341
|
+
|
342
|
+
begin
|
343
|
+
win=container.document_object.parentWindow
|
344
|
+
rescue WIN32OLERuntimeError
|
345
|
+
# if the document_object no longer has a parentWindow, we don't exist. if that's not the error, it's unexpected; raise.
|
346
|
+
if $!.message =~ /unknown property or method `parentWindow'/
|
347
|
+
return false
|
348
|
+
else
|
349
|
+
raise
|
350
|
+
end
|
351
|
+
end
|
352
|
+
document_object=win.document # I don't know why container.document_object != container.document_object.parentWindow.document
|
353
|
+
|
354
|
+
# we need a javascript function to test equality because comparing two WIN32OLEs always returns false (unless they have the same object_id, which these don't)
|
355
|
+
win.execScript("__watir_javascript_equals__=function(a, b){return a==b;}")
|
356
|
+
|
357
|
+
current_node=@element_object
|
358
|
+
while current_node
|
359
|
+
begin
|
360
|
+
if win.__watir_javascript_equals__(current_node, document_object)
|
361
|
+
# if we encounter the correct current document going up the parentNodes, @element_object does exist.
|
362
|
+
return true
|
363
|
+
end
|
364
|
+
current_node=current_node.parentNode
|
365
|
+
rescue WIN32OLERuntimeError
|
366
|
+
# two possibilities for why we're here:
|
367
|
+
# if the method __watir_javascript_equals__ stops existing, then that probably means the window changed, meaning @element object doesn't exist anymore.
|
368
|
+
# if we encounter an error trying to access parentNode before reaching the current document, @element_object doesn't exist.
|
369
|
+
return false
|
370
|
+
end
|
371
|
+
end
|
372
|
+
# if we escaped that loop, parentNode returned nil without encountering the current document; @element_object doesn't exist.
|
373
|
+
return false
|
374
|
+
end
|
375
|
+
end
|
376
|
+
end
|