vapir-ie 1.7.2 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/vapir-ie.rb CHANGED
@@ -6,23 +6,8 @@ module Vapir
6
6
  IE= Class.new(Vapir::Browser)
7
7
  end
8
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'
9
+ require 'vapir-ie/config'
10
+ require 'vapir-ie/browser'
26
11
  require 'vapir-ie/elements'
27
12
  require 'vapir-ie/version'
28
13
 
@@ -31,14 +16,5 @@ require 'vapir-common/waiter'
31
16
  module Vapir
32
17
  include Vapir::Exception
33
18
 
34
- # Directory containing the watir.rb file
35
- @@dir = File.expand_path(File.dirname(__FILE__))
36
19
 
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
20
  end
@@ -11,50 +11,6 @@ module Vapir
11
11
  include Vapir::Exception
12
12
  include IE::PageContainer
13
13
 
14
- # Maximum number of seconds to wait when attaching to a window
15
- @@attach_timeout = 2.0 # default value
16
- def self.attach_timeout
17
- @@attach_timeout
18
- end
19
- def self.attach_timeout=(timeout)
20
- @@attach_timeout = timeout
21
- end
22
-
23
- # Return the options used when creating new instances of IE.
24
- # BUG: this interface invites misunderstanding/misuse such as IE.options[:speed] = :zippy]
25
- def self.options
26
- {:speed => self.speed, :visible => self.visible, :attach_timeout => self.attach_timeout}
27
- end
28
- # set values for options used when creating new instances of IE.
29
- def self.set_options options
30
- options.each do |name, value|
31
- send "#{name}=", value
32
- end
33
- end
34
- # The globals $FAST_SPEED and $HIDE_IE are checked both at initialization
35
- # and later, because they
36
- # might be set after initialization. Setting them beforehand (e.g. from
37
- # the command line) will affect the class, otherwise it is only a temporary
38
- # effect
39
- @@speed = $FAST_SPEED ? :fast : :slow
40
- def self.speed
41
- return :fast if $FAST_SPEED
42
- @@speed
43
- end
44
- def self.speed= x
45
- $FAST_SPEED = nil
46
- @@speed = x
47
- end
48
- @@visible = $HIDE_IE ? false : true
49
- def self.visible
50
- return false if $HIDE_IE
51
- @@visible
52
- end
53
- def self.visible= x
54
- $HIDE_IE = nil
55
- @@visible = x
56
- end
57
-
58
14
  # IE inserts some element whose tagName is empty and just acts as block level element
59
15
  # Probably some IE method of cleaning things
60
16
  # To pass the same to the xml parser we need to give some name to empty tagName
@@ -64,257 +20,179 @@ module Vapir
64
20
  # the last command
65
21
  attr_reader :down_load_time
66
22
 
67
- # the OLE Internet Explorer object
68
- attr_accessor :ie
69
-
70
23
  # access to the logger object
71
24
  attr_accessor :logger
72
25
 
73
26
  # this contains the list of unique urls that have been visited
74
27
  attr_reader :url_list
75
28
 
76
- # Create a new IE window. Works just like IE.new in Watir 1.4.
77
- def self.new_window
78
- ie = new true
79
- ie._new_window_init
80
- ie
29
+ class << self
30
+ # Create a new IE window in a new process.
31
+ # This method will not work when
32
+ # Vapir/Ruby is run under a service (instead of a user).
33
+ def new_process(options={})
34
+ new(options.merge(:new_process => true))
35
+ end
36
+
37
+ # Create a new IE window in a new process, starting at the specified URL.
38
+ # Same as IE.start.
39
+ def start_process(url='about:blank', options={})
40
+ new(options.merge(:new_process => true, :goto => url))
41
+ end
42
+
43
+ # Yields successively to each IE window on the current desktop. Takes a block.
44
+ # This method will not work when
45
+ # Vapir/Ruby is run under a service (instead of a user).
46
+ # Yields to the window and its hwnd.
47
+ def each_browser
48
+ each_browser_object do |browser_object|
49
+ yield attach(:browser_object, browser_object)
50
+ end
51
+ end
52
+ alias each each_browser
53
+ def browsers
54
+ Enumerator.new(self, :each_browser)
55
+ end
56
+
57
+ # yields a WIN32OLE of each IE browser object that is available.
58
+ def each_browser_object
59
+ shell = WIN32OLE.new('Shell.Application')
60
+ shell.Windows.each do |window|
61
+ if (window.path =~ /Internet Explorer/ rescue false) && (window.hwnd rescue false)
62
+ yield window
63
+ end
64
+ end
65
+ end
66
+ def browser_objects
67
+ Enumerator.new(self, :each_browser_object)
68
+ end
69
+
81
70
  end
82
71
 
83
72
  # Create an IE browser.
84
- def initialize suppress_new_window=nil
85
- _new_window_init unless suppress_new_window
86
- end
87
-
88
- def _new_window_init
89
- create_browser_window
90
- initialize_options
91
- goto 'about:blank' # this avoids numerous problems caused by lack of a document
92
- end
93
-
94
- # Create a new IE Window, starting at the specified url.
95
- # If no url is given, start empty.
96
- def self.start url=nil
97
- start_window url
98
- end
99
-
100
- # Create a new IE window, starting at the specified url.
101
- # If no url is given, start empty. Works like IE.start in Watir 1.4.
102
- def self.start_window url=nil
103
- ie = new_window
104
- ie.goto url if url
105
- ie
106
- end
107
-
108
- # Create a new IE window in a new process.
109
- # This method will not work when
110
- # Vapir/Ruby is run under a service (instead of a user).
111
- def self.new_process
112
- ie = new true
113
- ie._new_process_init
114
- ie
115
- end
116
-
117
- def _new_process_init
118
- iep = Process.start
119
- @ie = iep.window
120
- @process_id = iep.process_id
121
- initialize_options
122
- goto 'about:blank'
123
- end
124
-
125
- # Create a new IE window in a new process, starting at the specified URL.
126
- # Same as IE.start.
127
- def self.start_process url=nil
128
- ie = new_process
129
- ie.goto url if url
130
- ie
131
- end
132
-
133
- # Return a Vapir::IE object for an existing IE window. Window can be
134
- # referenced by url, title, or window handle.
135
- # Second argument can be either a string or a regular expression in the
136
- # case of of :url or :title.
137
- # IE.attach(:url, 'http://www.google.com')
138
- # IE.attach(:title, 'Google')
139
- # IE.attach(:hwnd, 528140)
140
- # This method will not work when
141
- # Vapir/Ruby is run under a service (instead of a user).
142
- def self.attach how, what
143
- ie = new true # don't create window
144
- ie._attach_init(how, what)
145
- ie
146
- end
147
-
148
- # this method is used internally to attach to an existing window
149
- def _attach_init how, what
150
- attach_browser_window how, what
151
- initialize_options
152
- wait
153
- end
154
-
155
- # Return an IE object that wraps the given window, typically obtained from
156
- # Shell.Application.windows.
157
- def self.bind window
158
- ie = new true
159
- ie.ie = window
160
- ie.initialize_options
161
- ie
162
- end
163
-
164
- def create_browser_window
165
- @ie = WIN32OLE.new('InternetExplorer.Application')
166
- end
167
- private :create_browser_window
168
-
169
- def initialize_options
170
- self.visible = IE.visible
171
- self.speed = IE.speed
73
+ #
74
+ # Takes a hash of options:
75
+ # - :timeout - the number of seconds to wait for a window to appear when
76
+ # attaching or launching.
77
+ # - :goto - a url to which the IE browser will navigate. by default this
78
+ # is 'about:blank' for newly-launched IE instances, and the default is
79
+ # not to navigate anywhere for attached instances.
80
+ # - :new_process - true or false, default is false. whether to launch this
81
+ # IE instance as its own process (this has no effect if :attach is
82
+ # specified)
83
+ # - :attach - a two-element Array of [how, what] where how is one of:
84
+ # - :title - a string or regexp matching the title of the browser that
85
+ # should be attached to.
86
+ # - :URL - a string or regexp matching the URL of the browser that
87
+ # should be attached to.
88
+ # - :HWND - specifies the HWND of the browser that should be attached to.
89
+ # - :name - the name of the window (as specified in the second argument to a
90
+ # window.open() javascript call)
91
+ # - :browser_object - this is generally just used internally. 'what'
92
+ # is a WIN32OLE object representing the browser.
93
+ # - :wait - true or false, default is true. whether to wait for the browser
94
+ # to be ready before continuing.
95
+ def initialize(method_options = {})
96
+ if method_options==true || method_options==false
97
+ raise NotImplementedError, "#{self.class.name}.new takes an options hash - passing a boolean for 'suppress_new_window' is no longer supported. Please see the documentation for #{self.class}.new"
98
+ end
99
+ options = options_from_config(method_options, {:timeout => :attach_timeout, :new_process => :ie_launch_new_process, :wait => :wait, :visible => :browser_visible}, [:attach, :goto])
172
100
 
173
- @element_object = nil
174
- @page_container = self
175
101
  @error_checkers = []
176
102
 
177
103
  @logger = DefaultLogger.new
178
104
  @url_list = []
179
- end
180
105
 
181
- # Specifies the speed that commands will be executed at. Choices are:
182
- # * :slow (default)
183
- # * :fast
184
- # * :zippy
185
- # With IE#speed= :zippy, text fields will be entered at once, instead of
186
- # character by character (default).
187
- def speed= how_fast
188
- case how_fast
189
- when :zippy then
190
- @typingspeed = 0
191
- @pause_after_wait = 0.01
192
- @type_keys = false
193
- @speed = :fast
194
- when :fast then
195
- @typingspeed = 0
196
- @pause_after_wait = 0.01
197
- @type_keys = true
198
- @speed = :fast
199
- when :slow then
200
- @typingspeed = 0.08
201
- @pause_after_wait = 0.1
202
- @type_keys = true
203
- @speed = :slow
106
+ if options[:attach]
107
+ how, what = *options[:attach]
108
+ if how== :browser_object
109
+ @browser_object = what
110
+ else
111
+ orig_how=how
112
+ hows={
113
+ :title => proc{|bo| bo.document.title },
114
+ :URL => proc{|bo| bo.locationURL },
115
+ :name => proc{|bo| bo.document.parentWindow.name },
116
+ :HWND => proc{|bo| Vapir::IE.fix_win32ole_hwnd(bo.HWND) },
117
+ }
118
+ how=hows.keys.detect{|h| h.to_s.downcase==orig_how.to_s.downcase}
119
+ raise ArgumentError, "how should be one of: #{hows.keys.inspect} (was #{orig_how.inspect})" unless how
120
+ @browser_object = ::Waiter.try_for(options[:timeout], :exception => NoMatchingWindowFoundException.new("Unable to locate a window with #{how} of #{what}")) do
121
+ self.class.browser_objects.detect do |browser_object|
122
+ begin
123
+ Vapir::fuzzy_match(hows[how].call(browser_object), what)
124
+ rescue WIN32OLERuntimeError, NoMethodError
125
+ false
126
+ end
127
+ end
128
+ end
129
+ end
130
+ if method_options.key?(:visible)
131
+ # only set visibility if it's explicitly in the options given to the method - don't set from config when using attach
132
+ self.visible= method_options[:visible]
133
+ end
204
134
  else
205
- raise ArgumentError, "Invalid speed: #{how_fast}"
135
+ if options[:new_process]
136
+ iep = Process.start
137
+ @browser_object = iep.browser_object(:timeout => options[:timeout])
138
+ @process_id = iep.process_id
139
+ else
140
+ @browser_object = WIN32OLE.new('InternetExplorer.Application')
141
+ end
142
+ self.visible= options[:visible]
143
+ goto('about:blank')
206
144
  end
207
- end
208
-
209
- def speed
210
- return @speed if @speed == :slow
211
- return @type_keys ? :fast : :zippy
212
- end
213
-
214
- # deprecated: use speed = :fast instead
215
- def set_fast_speed
216
- self.speed = :fast
145
+ goto(options[:goto]) if options[:goto]
146
+ wait if options[:wait]
147
+ self
217
148
  end
218
149
 
219
- # deprecated: use speed = :slow instead
220
- def set_slow_speed
221
- self.speed = :slow
222
- end
223
-
224
150
  def visible
225
151
  assert_exists
226
- @ie.visible
152
+ @browser_object.visible
227
153
  end
228
- def visible=(boolean)
154
+ def visible=(visibility)
229
155
  assert_exists
230
- @ie.visible = boolean if boolean != @ie.visible
231
- end
232
-
233
- # Yields successively to each IE window on the current desktop. Takes a block.
234
- # This method will not work when
235
- # Vapir/Ruby is run under a service (instead of a user).
236
- # Yields to the window and its hwnd.
237
- def self.each
238
- shell = WIN32OLE.new('Shell.Application')
239
- shell.Windows.each do |window|
240
- next unless (window.path =~ /Internet Explorer/ rescue false)
241
- next unless (hwnd = window.hwnd rescue false)
242
- ie = IE.bind(window)
243
- ie.hwnd = hwnd
244
- yield ie
245
- end
246
- end
247
-
248
- # return internet explorer instance as specified. if none is found,
249
- # return nil.
250
- # arguments:
251
- # :url, url -- the URL of the IE browser window
252
- # :title, title -- the title of the browser page
253
- # :hwnd, hwnd -- the window handle of the browser window.
254
- # This method will not work when
255
- # Vapir/Ruby is run under a service (instead of a user).
256
- def self.find(how, what)
257
- ie_ole = IE._find(how, what)
258
- IE.bind ie_ole if ie_ole
259
- end
260
-
261
- def self._find(how, what)
262
- ieTemp = nil
263
- IE.each do |ie|
264
- window = ie.ie
265
-
266
- case how
267
- when :url
268
- ieTemp = window if Vapir::fuzzy_match(window.locationURL, what)
269
- when :title
270
- # normal windows explorer shells do not have document
271
- # note window.document will fail for "new" browsers
272
- begin
273
- title = window.locationname
274
- title = window.document.title
275
- rescue WIN32OLERuntimeError, NoMethodError
276
- end
277
- ieTemp = window if Vapir::fuzzy_match(title, what)
278
- when :hwnd
279
- begin
280
- ieTemp = window if what == window.HWND
281
- rescue WIN32OLERuntimeError, NoMethodError
282
- end
283
- else
284
- raise ArgumentError
285
- end
286
- end
287
- return ieTemp
156
+ @browser_object.visible = visibility
288
157
  end
289
158
 
290
- def attach_browser_window how, what
291
- log "Seeking Window with #{how}: #{what}"
292
- ieTemp = nil
293
- begin
294
- Vapir::until_with_timeout do
295
- ieTemp = IE._find how, what
296
- end
297
- rescue TimeOutException
298
- raise NoMatchingWindowFoundException,
299
- "Unable to locate a window with #{how} of #{what}"
300
- end
301
- @ie = ieTemp
302
- end
303
- private :attach_browser_window
159
+ # the WIN32OLE Internet Explorer object
160
+ #
161
+ # See: http://msdn.microsoft.com/en-us/library/aa752085%28v=VS.85%29.aspx
162
+ # and http://msdn.microsoft.com/en-us/library/aa752084%28v=VS.85%29.aspx
163
+ attr_reader :browser_object
164
+ alias ie browser_object
304
165
 
305
- def browser_object
166
+ # Return the window handle of this browser
167
+ def hwnd
306
168
  assert_exists
307
- @ie
169
+ @hwnd ||= Vapir::IE.fix_win32ole_hwnd(@browser_object.HWND)
308
170
  end
309
171
 
310
- # Return the current window handle
311
- def hwnd
312
- assert_exists
313
- @hwnd ||= @ie.hwnd
172
+ # win32ole's #HWND method casts to signed somewhere along its way, so returns
173
+ # a negative number. this is wrong, WinWindow will reject it (correctly), so we
174
+ # need to fix.
175
+ def self.fix_win32ole_hwnd(win32ole_hwnd)
176
+ win32ole_hwnd < 0 ? win32ole_hwnd % 2**32 : win32ole_hwnd
177
+ end
178
+
179
+ # returns the process id of this browser
180
+ def process_id
181
+ @process_id ||= win_window.process_id
182
+ end
183
+ # kills this process. NOTE that this process may be running
184
+ # multiple browsers; killing the process will kill them all.
185
+ # use #close to close a single browser.
186
+ def kill
187
+ # todo: drop win32api; use ffi
188
+ require 'Win32API'
189
+ right_to_terminate_process = 1
190
+ handle = Win32API.new('kernel32.dll', 'OpenProcess', 'lil', 'l').call(right_to_terminate_process, 0, process_id)
191
+ Win32API.new('kernel32.dll', 'TerminateProcess', 'll', 'l').call(handle, 0)
314
192
  end
315
- attr_writer :hwnd
316
193
 
317
194
  def win_window
195
+ Vapir.require_winwindow
318
196
  @win_window||= WinWindow.new(hwnd)
319
197
  end
320
198
 
@@ -343,8 +221,8 @@ module Vapir
343
221
 
344
222
  # Are we attached to an open browser?
345
223
  def exists?
346
- !!(@ie && begin
347
- @ie.name
224
+ !!(@browser_object && begin
225
+ @browser_object.name
348
226
  rescue WIN32OLERuntimeError, NoMethodError
349
227
  raise unless $!.message =~ ExistenceFailureCodesRE
350
228
  false
@@ -367,12 +245,12 @@ module Vapir
367
245
 
368
246
  # Return the title of the document
369
247
  def title
370
- @ie.document.title
248
+ @browser_object.document.title
371
249
  end
372
250
 
373
251
  # Return the status of the window, typically from the status bar at the bottom.
374
252
  def status
375
- return @ie.statusText
253
+ return @browser_object.statusText
376
254
  end
377
255
 
378
256
  #
@@ -383,7 +261,7 @@ module Vapir
383
261
  # * url - string - the URL to navigate to
384
262
  def goto(url)
385
263
  assert_exists do
386
- @ie.navigate(url)
264
+ @browser_object.navigate(url)
387
265
  wait
388
266
  return @down_load_time
389
267
  end
@@ -393,7 +271,7 @@ module Vapir
393
271
  # an WIN32OLERuntimeError exception is raised if the browser cant go back
394
272
  def back
395
273
  assert_exists do
396
- @ie.GoBack
274
+ @browser_object.GoBack
397
275
  wait
398
276
  end
399
277
  end
@@ -402,7 +280,7 @@ module Vapir
402
280
  # an WIN32OLERuntimeError exception is raised if the browser cant go forward
403
281
  def forward
404
282
  assert_exists do
405
- @ie.GoForward
283
+ @browser_object.GoForward
406
284
  wait
407
285
  end
408
286
  end
@@ -417,7 +295,7 @@ module Vapir
417
295
  # an WIN32OLERuntimeError exception is raised if the browser cant refresh
418
296
  def refresh
419
297
  assert_exists do
420
- @ie.refresh2(RefreshConstants::REFRESH_COMPLETELY)
298
+ @browser_object.refresh2(RefreshConstants::REFRESH_COMPLETELY)
421
299
  wait
422
300
  end
423
301
  end
@@ -430,13 +308,13 @@ module Vapir
430
308
  # Closes the Browser
431
309
  def close
432
310
  assert_exists
433
- @ie.stop
434
- @ie.quit
311
+ @browser_object.stop
312
+ @browser_object.quit
435
313
  # TODO/fix timeout; this shouldn't be a hard-coded magic number.
436
314
  ::Waiter.try_for(32, :exception => WindowFailedToCloseException.new("The browser window did not close"), :interval => 1) do
437
315
  begin
438
316
  if exists?
439
- @ie.quit
317
+ @browser_object.quit
440
318
  false
441
319
  else
442
320
  true
@@ -446,7 +324,7 @@ module Vapir
446
324
  true
447
325
  end
448
326
  end
449
- @ie=nil
327
+ @browser_object=nil
450
328
  end
451
329
 
452
330
  # Maximize the window (expands to fill the screen)
@@ -486,18 +364,22 @@ module Vapir
486
364
 
487
365
  # saves a screenshot of this browser window to the given filename.
488
366
  #
489
- # second argument, optional, specifies what area to take a screenshot of.
490
- # - :client takes a screenshot of the client area, which excludes the menu bar and other window trimmings.
491
- # - :window (default) takes a screenshot of the full browser window
492
- # - :desktop takes a screenshot of the full desktop
493
- def screen_capture(filename, dc=:window)
494
- if dc==:desktop
495
- screenshot_win=WinWindow.desktop_window
496
- dc=:window
497
- else
498
- screenshot_win=win_window
367
+ # the screenshot format is BMP (DIB), so you should name your files to end with .bmp
368
+ #
369
+ # the last argument is an optional options hash, taking options:
370
+ # - :dc => (default is :window). may be one of:
371
+ # - :client takes a screenshot of the client area, which excludes the menu bar and other window trimmings.
372
+ # - :window (default) takes a screenshot of the full browser window
373
+ # - :desktop takes a screenshot of the full desktop
374
+ def screen_capture(filename, options = {})
375
+ unless options.is_a?(Hash)
376
+ dc = options
377
+ options = {:dc => dc}
378
+ if config.warn_deprecated
379
+ Kernel.warn_with_caller("WARNING: The API for #screen_capture has changed and the last argument is now an options hash. Please change calls to this method to specify :dc => #{dc.inspect}")
380
+ end
499
381
  end
500
- screenshot_win.capture_to_bmp_file(filename, :dc => dc)
382
+ screen_capture_win_window(filename, options)
501
383
  end
502
384
 
503
385
  def dir
@@ -511,7 +393,7 @@ module Vapir
511
393
  # Return the current document
512
394
  def document
513
395
  assert_exists
514
- return @ie.document
396
+ return @browser_object.document
515
397
  end
516
398
  alias document_object document
517
399
 
@@ -522,7 +404,7 @@ module Vapir
522
404
  # returns the current url, as displayed in the address bar of the browser
523
405
  def url
524
406
  assert_exists
525
- return @ie.LocationURL
407
+ return @browser_object.LocationURL
526
408
  end
527
409
 
528
410
  # Error checkers
@@ -778,12 +660,7 @@ module Vapir
778
660
  temp = element_by_absolute_xpath(modifiedXpath) # temp = a DOM/COM element
779
661
  selectedElements << temp if temp != nil
780
662
  end
781
- #puts selectedElements.length
782
- if selectedElements.length == 0
783
- return nil
784
- else
785
- return selectedElements
786
- end
663
+ return selectedElements
787
664
  end
788
665
 
789
666
  # Method that iterates over IE DOM object and get the elements for the given
@@ -875,4 +752,21 @@ module Vapir
875
752
  end
876
753
 
877
754
  end # class IE
755
+ module WatirConfigCompatibility
756
+ module Visible
757
+ def visible
758
+ if config.warn_deprecated
759
+ Kernel.warn_with_caller "WARNING: #visible is deprecated; please use the new config framework with config.browser_visible"
760
+ end
761
+ config.browser_visible
762
+ end
763
+ def visible= visibility
764
+ if config.warn_deprecated
765
+ Kernel.warn_with_caller "WARNING: #visible= is deprecated; please use the new config framework with config.browser_visible="
766
+ end
767
+ config.browser_visible=visibility
768
+ end
769
+ end
770
+ Vapir::IE.send(:extend, Visible)
771
+ end
878
772
  end