vapir-ie 1.7.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,77 @@
1
+ module Vapir
2
+
3
+ class IE::Pre < IE::Element
4
+ include Vapir::Pre
5
+ end
6
+
7
+ class IE::P < IE::Element
8
+ include Vapir::P
9
+ end
10
+
11
+ # this class is used to deal with Div tags in the html page. http://msdn.microsoft.com/workshop/author/dhtml/reference/objects/div.asp?frame=true
12
+ # It would not normally be created by users
13
+ class IE::Div < IE::Element
14
+ include Vapir::Div
15
+ end
16
+
17
+ # this class is used to deal with Span tags in the html page. It would not normally be created by users
18
+ class IE::Span < IE::Element
19
+ include Vapir::Span
20
+ end
21
+
22
+ class IE::Map < IE::Element
23
+ include Vapir::Map
24
+ end
25
+
26
+ class IE::Area < IE::Element
27
+ include Vapir::Area
28
+ end
29
+
30
+ # Accesses Label element on the html page - http://msdn.microsoft.com/workshop/author/dhtml/reference/objects/label.asp?frame=true
31
+ class IE::Label < IE::Element
32
+ include Vapir::Label
33
+ end
34
+
35
+ class IE::Li < IE::Element
36
+ include Vapir::Li
37
+ end
38
+ class IE::Ul < IE::Element
39
+ include Vapir::Ul
40
+ end
41
+ class IE::Ol < IE::Element
42
+ include Vapir::Ol
43
+ end
44
+ class IE::H1 < IE::Element
45
+ include Vapir::H1
46
+ end
47
+ class IE::H2 < IE::Element
48
+ include Vapir::H2
49
+ end
50
+ class IE::H3 < IE::Element
51
+ include Vapir::H3
52
+ end
53
+ class IE::H4 < IE::Element
54
+ include Vapir::H4
55
+ end
56
+ class IE::H5 < IE::Element
57
+ include Vapir::H5
58
+ end
59
+ class IE::H6 < IE::Element
60
+ include Vapir::H6
61
+ end
62
+ class IE::Dl < IE::Element
63
+ include Vapir::Dl
64
+ end
65
+ class IE::Dt < IE::Element
66
+ include Vapir::Dt
67
+ end
68
+ class IE::Dd < IE::Element
69
+ include Vapir::Dd
70
+ end
71
+ class IE::Strong < IE::Element
72
+ include Vapir::Strong
73
+ end
74
+ class IE::Em < IE::Element
75
+ include Vapir::Em
76
+ end
77
+ end
@@ -0,0 +1,203 @@
1
+ require 'vapir-ie/container'
2
+
3
+ module Vapir
4
+ # A PageContainer contains an HTML Document. In other words, it is a
5
+ # what JavaScript calls a Window.
6
+ #
7
+ # this assumes that document_object is defined on the includer.
8
+ module IE::PageContainer
9
+ # Used internally to determine when IE has finished loading a page
10
+ # http://msdn.microsoft.com/en-us/library/system.windows.forms.webbrowserreadystate.aspx
11
+ # http://msdn.microsoft.com/en-us/library/system.windows.forms.webbrowser.readystate.aspx
12
+ module WebBrowserReadyState
13
+ Uninitialized = 0 # No document is currently loaded.
14
+ Loading = 1 # The control is loading a new document.
15
+ Loaded = 2 # The control has loaded and initialized the new document, but has not yet received all the document data.
16
+ Interactive = 3 # The control has loaded enough of the document to allow limited user interaction, such as clicking hyperlinks that have been displayed.
17
+ Complete = 4 # The control has finished loading the new document and all its contents.
18
+ end
19
+ READYSTATE_COMPLETE = WebBrowserReadyState::Complete
20
+
21
+ def containing_object
22
+ document_object
23
+ end
24
+ include IE::Container
25
+ include Vapir::Exception
26
+
27
+ # This method checks the currently displayed page for http errors, 404, 500 etc
28
+ # It gets called internally by the wait method, so a user does not need to call it explicitly
29
+ def check_for_http_error
30
+ # check for IE7
31
+ n = self.document.invoke('parentWindow').navigator.appVersion
32
+ m=/MSIE\s(.*?);/.match( n )
33
+ if m and m[1] =='7.0'
34
+ if m = /HTTP (\d\d\d.*)/.match( self.title )
35
+ raise NavigationException, m[1]
36
+ end
37
+ else
38
+ # assume its IE6
39
+ url = self.document.location.href
40
+ if /shdoclc.dll/.match(url)
41
+ m = /id=IEText.*?>(.*?)</i.match(self.html)
42
+ raise NavigationException, m[1] if m
43
+ end
44
+ end
45
+ false
46
+ end
47
+
48
+ def document_element
49
+ document_object.documentElement
50
+ end
51
+ def content_window_object
52
+ document_object.parentWindow
53
+ end
54
+
55
+ def page_container
56
+ self
57
+ end
58
+
59
+ # The HTML of the current page
60
+ def html
61
+ document_element.outerHTML
62
+ end
63
+
64
+ # The url of the page object.
65
+ def url
66
+ document_object.location.href
67
+ end
68
+
69
+ # The text of the current page
70
+ def text
71
+ document_element.innerText
72
+ end
73
+
74
+ def close
75
+ content_window_object.close
76
+ end
77
+
78
+ def title
79
+ document_object.title
80
+ end
81
+
82
+ # Execute the given JavaScript string
83
+ def execute_script(source)
84
+ retried=false
85
+ result=nil
86
+ begin
87
+ result=document_object.parentWindow.eval(source)
88
+ rescue WIN32OLERuntimeError
89
+ # don't retry more than once; don't catch anything but the particular thing we're looking for
90
+ if retried || $!.message !~ /unknown property or method:? `eval'/
91
+ raise
92
+ end
93
+ # this can happen if no scripts have executed at all - the 'eval' function doesn't exist.
94
+ # execScript works, but behaves differently than eval (it doesn't return anything) - but
95
+ # once an execScript has run, eval is subsequently defined. so, execScript a blank script,
96
+ # and then try again with eval.
97
+ document.parentWindow.execScript('null')
98
+ retried=true
99
+ retry
100
+ end
101
+ return result
102
+ end
103
+
104
+ # Block execution until the page has loaded.
105
+ # =nodoc
106
+ # Note: This code needs to be prepared for the ie object to be closed at
107
+ # any moment!
108
+ def wait(options={})
109
+ unless options.is_a?(Hash)
110
+ raise ArgumentError, "given options should be a Hash, not #{options.inspect} (#{options.class})\nold conflicting arguments of no_sleep or last_url are gone"
111
+ end
112
+ options={:sleep => false, :interval => 0.1, :timeout => 120}.merge(options)
113
+ @xml_parser_doc = nil
114
+ @down_load_time = nil
115
+ start_load_time = Time.now
116
+
117
+ if respond_to?(:browser_object)
118
+ ::Waiter.try_for(options[:timeout]-(Time.now-start_load_time), :interval => options[:interval], :exception => "The browser was still busy at the end of the specified interval") do
119
+ return unless exists?
120
+ !browser_object.busy
121
+ end
122
+ ::Waiter.try_for(options[:timeout]-(Time.now-start_load_time), :interval => options[:interval], :exception => "The browser's readyState was still not ready for interaction at the end of the specified interval") do
123
+ return unless exists?
124
+ [WebBrowserReadyState::Interactive, WebBrowserReadyState::Complete].include?(browser_object.readyState)
125
+ end
126
+ end
127
+ ::Waiter.try_for(options[:timeout]-(Time.now-start_load_time), :interval => options[:interval], :exception => "The browser's document was still not defined at the end of the specified interval") do
128
+ return unless exists?
129
+ document_object
130
+ end
131
+ urls=[]
132
+ all_frames_complete_result=::Waiter.try_for(options[:timeout]-(Time.now-start_load_time), :interval => options[:interval], :exception => nil, :condition => proc{|result| result==true }) do
133
+ return unless exists?
134
+ all_frames_complete?(document_object, urls)
135
+ end
136
+ case all_frames_complete_result
137
+ when false
138
+ raise "A frame on the browser did not come into readyState complete by the end of the specified interval"
139
+ when Exception
140
+ message = "A frame on the browser encountered an error.\n"
141
+ if all_frames_complete_result.message =~ /0x80070005/
142
+ message += "An 'Access is denied' error might be fixed by adding the domain of the site to your 'Trusted Sites'.\n"
143
+ end
144
+ message+="Original message was:\n\n"
145
+ message+=all_frames_complete_result.message
146
+ raise all_frames_complete_result.class, message, all_frames_complete_result.backtrace
147
+ when true
148
+ # dandy; carry on.
149
+ else
150
+ # this should never happen.
151
+ raise "Unexpected result from all_frames_complete?: #{all_frames_complete_result.inspect}"
152
+ end
153
+ @url_list=(@url_list || [])+urls
154
+
155
+ @down_load_time= Time.now - start_load_time
156
+ run_error_checks if respond_to?(:run_error_checks)
157
+ sleep @pause_after_wait if options[:sleep]
158
+ @down_load_time
159
+ end
160
+
161
+ private
162
+ # this returns true if all frames are complete.
163
+ # it returns false if a frame is incomplete.
164
+ # if an unexpected exception is encountered, it returns that exception. yes, returns, not raises,
165
+ # due to the fact that an exception may indicate either the frame not being complete, or an actual
166
+ # error condition - it is difficult to differentiate. in the usage above, in #wait, we check
167
+ # if an exception is still being raised at the end of the specified interval, and raise it if so.
168
+ # if it stops being raised, we carry on.
169
+ def all_frames_complete?(document, urls=nil)
170
+ begin
171
+ if urls && !urls.include?(document.location.href)
172
+ urls << document.location.href
173
+ end
174
+ frames=document.frames
175
+ return document.readyState=='complete' && (0...frames.length).all? do |i|
176
+ frame=document.frames[i.to_s]
177
+ frame_document=begin
178
+ frame.document
179
+ rescue WIN32OLERuntimeError
180
+ $!
181
+ end
182
+ case frame_document
183
+ when nil
184
+ # frame hasn't loaded to the point where it has a document yet
185
+ false
186
+ when WIN32OLE
187
+ # frame has a document - check recursively
188
+ all_frames_complete?(frame_document, urls)
189
+ when WIN32OLERuntimeError
190
+ # if we get a WIN32OLERuntimeError with access denied, that is probably a 404 and it's not going
191
+ # to load, so no reason to keep waiting for it - consider it 'complete' and return true.
192
+ # there's probably a better method of determining this but I haven't found it yet.
193
+ true
194
+ else # don't know what we'd have here
195
+ raise RuntimeError, "unknown frame.document: #{frame_document.inspect} (#{frame_document.class})"
196
+ end
197
+ end
198
+ rescue WIN32OLERuntimeError
199
+ return $!
200
+ end
201
+ end
202
+ end # module
203
+ end
@@ -0,0 +1,22 @@
1
+ require 'vapir-ie/ie-class'
2
+
3
+ module Vapir
4
+ module Process
5
+
6
+ # Returns the number of windows processes running with the specified name.
7
+ def self.count name
8
+ mgmt = WIN32OLE.connect('winmgmts:\\\\.')
9
+ processes = mgmt.InstancesOf('win32_process')
10
+ processes.extend Enumerable
11
+ processes.select{|x| x.name == name}.length
12
+ end
13
+
14
+ end
15
+
16
+ class IE
17
+ # Returns the number of IEXPLORE processes currently running.
18
+ def self.process_count
19
+ Vapir::Process.count 'iexplore.exe'
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,7 @@
1
+ module Vapir
2
+ module ScreenCapture
3
+ def screen_capture(filename , active_window_only=false, save_as_bmp=false)
4
+ raise NotImplementedError, "This method is gone. Please instead see IE#screen_capture(filename)"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,79 @@
1
+ at_exit do
2
+ if $!
3
+ err={:class => $!.class, :message => $!.message, :backtrace => $!.backtrace}
4
+ if $upload_dialog && $upload_dialog.exists?
5
+ # if the upload dialog still exists, we need to close it so that the #click WIN32OLE call in the parent process can return.
6
+ if (upload_dialog_popup=$upload_dialog.enabled_popup)
7
+ # we will assume this popup is of the "The above file name is invalid" variety, so click 'OK'
8
+ contents=begin
9
+ upload_dialog_popup.children.select{|child| child.class_name=='Static' && child.text!=''}.map{|child| child.text}.join(' ')
10
+ rescue WinWindow::Error
11
+ "{Unable to retrieve contents}"
12
+ end
13
+ err[:message]+="\n\nA popup was found on the dialog with title: \n#{upload_dialog_popup.text.inspect}\nand text contents: \n#{contents}"
14
+ if upload_dialog_popup.click_child_button_try_for!('OK', 4, :exception => nil)
15
+ err[:message]+="\n\nClicked 'OK' on the popup."
16
+ end
17
+ end
18
+ # once the popup is gone (or if there wasn't one - this happens if the field was set to blank, or set to a directory that exists)
19
+ # then click 'cancel'
20
+ if $upload_dialog.click_child_button_try_for!('Cancel', 4, :exception => nil)
21
+ err[:message]+="\n\nClicked the 'Cancel' button instead of 'Open' on the File Upload dialog."
22
+ end
23
+
24
+ # none of the above are expected to error, but maybe should be in a begin/rescue just in case?
25
+ end
26
+ if $error_file_name && $error_file_name != ''
27
+ File.open($error_file_name, 'w') do |error_file|
28
+ to_write=Marshal.dump(err)
29
+ error_file.write(to_write)
30
+ end
31
+ end
32
+ else
33
+ if $error_file_name && $error_file_name != '' && File.exists?($error_file_name)
34
+ File.delete($error_file_name)
35
+ # the file not existing indicates success.
36
+ end
37
+ end
38
+ end
39
+
40
+ $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'vapir-common', 'lib'))
41
+
42
+ require 'vapir-common/win_window'
43
+ require 'vapir-common/waiter'
44
+ require 'vapir-common/exceptions'
45
+
46
+ browser_hwnd, file_path, $error_file_name=*ARGV
47
+ unless (2..3).include?(ARGV.size) && browser_hwnd =~ /^\d+$/ && browser_hwnd.to_i > 0
48
+ raise ArgumentError, "This script takes two or three arguments: the hWnd that the File Selection dialog will pop up on (positive integer); the path to the file to select; and (optional) a filename to write any failure message to."
49
+ end
50
+
51
+ # titles of file upload window titles in supported browsers
52
+ # Add to this titles in other languages, too
53
+ UploadWindowTitles= { :IE8 => "Choose File to Upload",
54
+ :IE7 => 'Choose file',
55
+ }
56
+ # list of arguments to give to WinWindow#child_control_with_preceding_label to find the filename field
57
+ # on dialogs of supported browsers (just the one right now because it's the same in ie7 and ie8)
58
+ # add to this stuff for other languages, too
59
+ UploadWindowFilenameFields = [["File &name:", {:control_class_name => 'ComboBoxEx32'}]]
60
+
61
+ browser_window=WinWindow.new(browser_hwnd.to_i)
62
+
63
+ popup=nil
64
+ $upload_dialog=::Waiter.try_for(16, :exception => nil) do
65
+ if (popup=browser_window.enabled_popup) && UploadWindowTitles.values.include?(popup.text)
66
+ popup
67
+ end
68
+ end
69
+ unless $upload_dialog
70
+ raise Vapir::Exception::NoMatchingWindowFoundException.new('No window found to upload a file - '+(popup ? "enabled popup exists but has unrecognized text #{popup.text}" : 'no popup is on the browser'))
71
+ end
72
+ filename_fields=UploadWindowFilenameFields.map do |control_args|
73
+ $upload_dialog.child_control_with_preceding_label(*control_args)
74
+ end
75
+ unless (filename_field=filename_fields.compact.first)
76
+ raise Vapir::Exception::NoMatchingWindowFoundException, "Could not find a filename field in the File Upload dialog"
77
+ end
78
+ filename_field.send_set_text! file_path
79
+ $upload_dialog.click_child_button_try_for!('Open', 4, :exception => WinWindow::Error.new("Failed to click the Open button on the File Upload dialog. It exists, but we couldn't click it."))
@@ -0,0 +1,33 @@
1
+ require 'vapir-ie/element'
2
+ require 'vapir-common/elements/elements'
3
+
4
+ module Vapir
5
+
6
+ # This class is used for dealing with tables.
7
+ # Normally a user would not need to create this object as it is returned by the Vapir::Container#table method
8
+ #
9
+ # many of the methods available to this object are inherited from the Element class
10
+ #
11
+ class IE::Table < IE::Element
12
+ include Vapir::Table
13
+
14
+ def self.create_from_element(container, element)
15
+ Vapir::Table.create_from_element(container, element)
16
+ end
17
+ end
18
+
19
+ # this class is a table body
20
+ class IE::TableBody < IE::Element
21
+ include Vapir::TBody
22
+ end
23
+
24
+ class IE::TableRow < IE::Element
25
+ include Vapir::TableRow
26
+ end
27
+
28
+ # this class is a table cell - when called via the Table object
29
+ class IE::TableCell < IE::Element
30
+ include Vapir::TableCell
31
+ end
32
+
33
+ end
@@ -0,0 +1,5 @@
1
+ module Vapir
2
+ class IE
3
+ VERSION = '1.7.0.rc1'
4
+ end
5
+ end
@@ -0,0 +1,45 @@
1
+ # load the correct version of win32ole
2
+
3
+ # Use our modified win32ole library
4
+
5
+ if RUBY_VERSION =~ /^1\.8/
6
+ $LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__), '..', 'vapir-ie', 'win32ole'))
7
+ else
8
+ # loading win32ole from stdlib on 1.9
9
+ end
10
+
11
+
12
+ require 'win32ole'
13
+
14
+ WIN32OLE.codepage = WIN32OLE::CP_UTF8
15
+
16
+ # necessary extension of win32ole
17
+ class WIN32OLE
18
+ def respond_to?(method)
19
+ super || object_respond_to?(method)
20
+ end
21
+
22
+ # checks if WIN32OLE#ole_method returns an WIN32OLE_METHOD, or errors.
23
+ # WARNING: #ole_method is pretty slow, and consequently so is this. you are likely to be better
24
+ # off just calling a method you are not sure exists, and rescuing the WIN32OLERuntimeError
25
+ # that is raised if it doesn't exist.
26
+ def object_respond_to?(method)
27
+ method=method.to_s
28
+ # strip assignment = from methods. going to assume that if it has a getter method, it will take assignment, too. this may not be correct, but will have to do.
29
+ if method =~ /=\z/
30
+ method=$`
31
+ end
32
+ respond_to_cache[method]
33
+ end
34
+
35
+ private
36
+ def respond_to_cache
37
+ @respond_to_cache||=Hash.new do |hash, key|
38
+ hash[key]=begin
39
+ !!self.ole_method(key)
40
+ rescue WIN32OLERuntimeError
41
+ false
42
+ end
43
+ end
44
+ end
45
+ end