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.
@@ -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