vapir-ie 1.7.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|