watir-classic 3.3.0 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/CHANGES +17 -0
  2. data/Gemfile.lock +9 -8
  3. data/LICENSE +1 -0
  4. data/README.rdoc +6 -7
  5. data/Rakefile +3 -1
  6. data/VERSION +1 -1
  7. data/lib/watir-classic.rb +0 -5
  8. data/lib/watir-classic/browser.rb +58 -35
  9. data/lib/watir-classic/browsers.rb +1 -1
  10. data/lib/watir-classic/container.rb +39 -33
  11. data/lib/watir-classic/cookies.rb +32 -2
  12. data/lib/watir-classic/core.rb +0 -1
  13. data/lib/watir-classic/dialogs/alert.rb +12 -0
  14. data/lib/watir-classic/dialogs/file_field.rb +11 -0
  15. data/lib/watir-classic/drag_and_drop_helper.rb +14 -0
  16. data/lib/watir-classic/element.rb +292 -257
  17. data/lib/watir-classic/element_collection.rb +26 -8
  18. data/lib/watir-classic/element_extensions.rb +22 -16
  19. data/lib/watir-classic/exceptions.rb +4 -4
  20. data/lib/watir-classic/form.rb +52 -49
  21. data/lib/watir-classic/frame.rb +23 -14
  22. data/lib/watir-classic/ie-class.rb +363 -315
  23. data/lib/watir-classic/ie-process.rb +1 -0
  24. data/lib/watir-classic/ie.rb +0 -17
  25. data/lib/watir-classic/image.rb +58 -64
  26. data/lib/watir-classic/input_elements.rb +224 -219
  27. data/lib/watir-classic/link.rb +14 -15
  28. data/lib/watir-classic/locator.rb +12 -7
  29. data/lib/watir-classic/matches.rb +7 -3
  30. data/lib/watir-classic/modal_dialog.rb +38 -26
  31. data/lib/watir-classic/non_control_elements.rb +29 -0
  32. data/lib/watir-classic/options.rb +10 -15
  33. data/lib/watir-classic/page-container.rb +30 -48
  34. data/lib/watir-classic/process.rb +4 -2
  35. data/lib/watir-classic/screenshot.rb +6 -0
  36. data/lib/watir-classic/supported_elements.rb +36 -14
  37. data/lib/watir-classic/table.rb +81 -71
  38. data/lib/watir-classic/util.rb +9 -11
  39. data/lib/watir-classic/wait.rb +17 -4
  40. data/lib/watir-classic/wait_helper.rb +15 -2
  41. data/lib/watir-classic/win32.rb +2 -1
  42. data/lib/watir-classic/window.rb +35 -7
  43. data/lib/watir-classic/xpath_locator.rb +1 -0
  44. data/lib/watir-classic/yard/global_macros.rb +7 -0
  45. data/spec/frame_spec.rb +17 -0
  46. metadata +5 -7
  47. data/lib/watir-classic/close_all.rb +0 -31
  48. data/lib/watir-classic/contrib/enabled_popup.rb +0 -21
  49. data/lib/watir-classic/contrib/ie-new-process.rb +0 -27
  50. data/lib/watir-classic/contrib/page_checker.rb +0 -29
  51. data/watir.gif +0 -0
@@ -1,11 +1,12 @@
1
1
  module Watir
2
- # this class is the super class for the iterator classes (buttons, links, spans etc
3
- # it would normally only be accessed by the iterator methods (spans, links etc) of IE
2
+ # This class is the super class for the iterator classes (buttons, links, spans etc).
3
+ # It would normally only be accessed by the iterator methods (spans, links etc) of {Container}.
4
4
  class ElementCollection
5
5
  include Enumerable
6
6
 
7
7
  # Super class for all the iterator classes
8
- # * container - an instance of an IE object
8
+ # @param [Element] container container element instance.
9
+ # @param [Hash] specifiers locators for elements.
9
10
  def initialize(container, specifiers)
10
11
  if specifiers[:index]
11
12
  raise Exception::MissingWayOfFindingObjectException,
@@ -17,6 +18,7 @@ module Watir
17
18
  @page_container = container.page_container
18
19
  end
19
20
 
21
+ # @return [Fixnum] count of elements in this collection.
20
22
  def length
21
23
  count = 0
22
24
  each {|element| count += 1 }
@@ -25,28 +27,36 @@ module Watir
25
27
 
26
28
  alias_method :size, :length
27
29
 
28
- # iterate through each of the elements in the collection in turn
30
+ # Iterate through each of the elements in the collection in turn.
31
+ # @yieldparam [Element] element element instance.
29
32
  def each
30
33
  @container.locator_for(TaggedElementLocator, @specifiers, element_class).each {|element| yield element}
31
34
  end
32
35
 
33
- # allows access to a specific item in the collection
36
+ # Access a specific item in the collection.
37
+ #
38
+ # @note {Element} will be always returned even if the index is out of
39
+ # bounds. Use {Element#exists?} to verify if the element actually exists.
40
+ # @param [Fixnum] n n-th element to retrieve.
41
+ # @return [Element] element with specified index from this collection.
34
42
  def [](n)
35
- number = n - Watir::IE.base_index
36
- offset = Watir::IE.zero_based_indexing ? (length - 1) : length
37
43
  non_existing_element = element_class.new(@container, @specifiers.merge(:index => n))
38
44
  def non_existing_element.locate; nil end
39
- iterator_object(number) || non_existing_element
45
+ iterator_object(n) || non_existing_element
40
46
  end
41
47
 
48
+ # @return [Element] first element from this collection.
42
49
  def first
43
50
  iterator_object(0)
44
51
  end
45
52
 
53
+ # @return [Element] last element from this collection.
46
54
  def last
47
55
  iterator_object(length - 1)
48
56
  end
49
57
 
58
+ # @return [String] String representation of each element in this collection
59
+ # separated by line-feed.
50
60
  def to_s
51
61
  map { |e| e.to_s }.join("\n")
52
62
  end
@@ -71,12 +81,14 @@ module Watir
71
81
 
72
82
  end
73
83
 
84
+ # This class represents table elements collection.
74
85
  class TableElementCollection < ElementCollection
75
86
  def initialize(container, specifiers, ole_collection=nil)
76
87
  super container, specifiers
77
88
  @ole_collection = ole_collection
78
89
  end
79
90
 
91
+ # @see ElementCollection#each
80
92
  def each
81
93
  if @ole_collection
82
94
  elements = []
@@ -90,17 +102,23 @@ module Watir
90
102
  end
91
103
  end
92
104
 
105
+ # This class represents table row elements collection.
93
106
  class TableRowCollection < TableElementCollection; end
94
107
 
108
+ # This class represents table cell elements collection.
95
109
  class TableCellCollection < TableElementCollection; end
96
110
 
111
+ # This class represents input elements collection.
97
112
  class InputElementCollection < ElementCollection
113
+ # @see ElementCollection#each
98
114
  def each
99
115
  @container.locator_for(InputElementLocator, @specifiers, element_class).each {|element| yield element}
100
116
  end
101
117
  end
102
118
 
119
+ # This class represents general elements collection.
103
120
  class HTMLElementCollection < ElementCollection
121
+ # @see ElementCollection#each
104
122
  def each
105
123
  @container.locator_for(TaggedElementLocator, @specifiers, Element).each { |element| yield element }
106
124
  end
@@ -1,14 +1,13 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Watir
4
- # This assumes that Element#visible? is defined
4
+ # This module adds some helper methods for asynchronous testing.
5
5
  module ElementExtensions
6
6
 
7
- #
8
- # Wraps a {Celerity,Watir}::Element so that any subsequent method calls are
7
+ # Wraps an {Element} so that any subsequent method calls are
9
8
  # put on hold until the element is present on the page.
10
9
  #
11
-
10
+ # @private
12
11
  class WhenPresentDecorator
13
12
  def initialize(element, timeout)
14
13
  @element = element
@@ -28,26 +27,25 @@ module Watir
28
27
 
29
28
  end
30
29
 
31
- #
32
- # Returns true if the element exists and is visible on the page
33
- #
34
-
30
+ # @return [Boolean] true when element exists and is visible, false otherwise.
35
31
  def present?
36
32
  exists? && visible? rescue false
37
33
  end
38
34
 
35
+ # Waits until the element is present before calling its methods.
39
36
  #
40
- # Waits until the element is present.
37
+ # @example
38
+ # browser.button(:id, 'foo').when_present.click
41
39
  #
42
- # Optional argument:
40
+ # @example
41
+ # browser.div(:id, 'bar').when_present { |div| ... }
43
42
  #
44
- # timeout - seconds to wait before timing out (default: 60)
43
+ # @example
44
+ # browser.p(:id, 'baz').when_present(60).text
45
45
  #
46
- # browser.button(:id, 'foo').when_present.click
47
- # browser.div(:id, 'bar').when_present { |div| ... }
48
- # browser.p(:id, 'baz').when_present(60).text
49
- #
50
-
46
+ # @param [Fixnum] timeout seconds to wait before timing out.
47
+ # @raise [Watir::Wait::TimeoutError] will be raised when element is not
48
+ # present within specified timeout.
51
49
  def when_present(timeout = 60)
52
50
  if block_given?
53
51
  Watir::Wait.until(timeout) { self.present? }
@@ -57,10 +55,18 @@ module Watir
57
55
  end
58
56
  end
59
57
 
58
+ # Wait until element is present before continuing.
59
+ #
60
+ # @raise [Watir::Wait::TimeoutError] will be raised when element is not
61
+ # present within specified timeout.
60
62
  def wait_until_present(timeout = 60)
61
63
  Watir::Wait.until(timeout) { self.present? }
62
64
  end
63
65
 
66
+ # Wait while element is present before continuing.
67
+ #
68
+ # @raise [Watir::Wait::TimeoutError] will be raised when element is still present
69
+ # after specified timeout.
64
70
  def wait_while_present(timeout = 60)
65
71
  Watir::Wait.while(timeout) { self.present? }
66
72
  end
@@ -3,11 +3,11 @@ module Watir
3
3
 
4
4
  # Root class for all Watir Exceptions
5
5
  class WatirException < RuntimeError
6
- def initialize(message="")
7
- super(message)
8
- end
6
+ def initialize(message="")
7
+ super(message)
8
+ end
9
9
  end
10
-
10
+
11
11
  # This exception is raised if an attempt is made to access an object that doesn't exist
12
12
  class UnknownObjectException < WatirException; end
13
13
  # This exception is raised if an attempt is made to access an object that is in a disabled state
@@ -1,24 +1,33 @@
1
1
  module Watir
2
2
 
3
+ # Returned by {Container#form}.
3
4
  class Form < Element
5
+
6
+ attr_ole :action
7
+
4
8
  def initialize(container, specifiers)
5
9
  super
6
10
  copy_test_config container
7
11
  end
8
12
 
9
- attr_ole :action
10
-
13
+ # @return [String] form name attribute value. Will be empty string if does not
14
+ # exist.
15
+ # @macro exists
11
16
  def name
12
17
  assert_exists
13
18
  name = ole_object.getAttributeNode('name')
14
19
  name ? name.value : ''
15
20
  end
16
21
 
22
+ # @return [String] form method attribute value.
23
+ # @macro exists
17
24
  def form_method
18
25
  assert_exists
19
26
  ole_object.invoke('method')
20
27
  end
21
-
28
+
29
+ # @param [Object] arg when argument is nil, {#form_method} will be called.
30
+ # Otherwise Ruby's {Kernel#method} will be called.
22
31
  def method(arg = nil)
23
32
  if arg.nil?
24
33
  form_method
@@ -27,68 +36,62 @@ module Watir
27
36
  end
28
37
  end
29
38
 
30
- def locate
31
- @o = @container.locator_for(FormLocator, @specifiers, self.class).locate
32
- end
33
-
34
- # Submit the data -- equivalent to pressing Enter or Return to submit a form.
39
+ # Submit the form.
40
+ # @note Will not submit the form if its onSubmit JavaScript callback
41
+ # returns false.
42
+ # @macro exists
35
43
  def submit
36
44
  assert_exists
37
45
  @o.submit(0) if dispatch_event "onSubmit"
38
46
  @container.wait
39
47
  end
40
-
48
+
49
+ # Flash the element the specified number of times for troubleshooting purposes.
50
+ # @param [Fixnum] number number of times to flash the element.
51
+ # @macro exists
52
+ def flash(number=10)
53
+ assert_exists
54
+ @original_element_colors = {}
55
+ number.times do
56
+ set_highlight
57
+ sleep 0.05
58
+ clear_highlight
59
+ sleep 0.05
60
+ end
61
+ end
62
+
63
+ # @private
64
+ def locate
65
+ @o = @container.locator_for(FormLocator, @specifiers, self.class).locate
66
+ end
67
+
68
+ # @private
41
69
  def __ole_inner_elements
42
70
  assert_exists
43
71
  @o.elements
44
72
  end
45
73
 
46
- # This method is responsible for setting and clearing the colored highlighting on the specified form.
47
- # use :set to set the highlight
48
- # :clear to clear the highlight
49
- def highlight(set_or_clear, element, count)
50
- if set_or_clear == :set
51
- begin
74
+ private
75
+
76
+ # This method is responsible for setting the colored highlighting on the specified form.
77
+ def set_highlight
78
+ @o.elements.each do |element|
79
+ perform_highlight do
52
80
  original_color = element.style.backgroundColor
53
- original_color = "" if original_color==nil
54
- element.style.backgroundColor = activeObjectHighLightColor
55
- rescue => e
56
- puts e
57
- puts e.backtrace.join("\n")
58
- original_color = ""
59
- end
60
- @original_styles[count] = original_color
61
- else
62
- begin
63
- element.style.backgroundColor = @original_styles[ count]
64
- rescue => e
65
- puts e
66
- # we could be here for a number of reasons...
67
- ensure
81
+ element.style.backgroundColor = active_object_highlight_color
82
+ @original_element_colors[element] = original_color
68
83
  end
69
84
  end
70
85
  end
71
- private :highlight
72
-
73
- # causes the object to flash. Normally used in IRB when creating scripts
74
- # Default is 10
75
- def flash number=10
76
- assert_exists
77
- @original_styles = {}
78
- number.times do
79
- count = 0
80
- @o.elements.each do |element|
81
- highlight(:set, element, count)
82
- count += 1
83
- end
84
- sleep 0.05
85
- count = 0
86
- @o.elements.each do |element|
87
- highlight(:clear, element, count)
88
- count += 1
86
+
87
+ # This method is responsible for clearing the colored highlighting on the specified form.
88
+ def clear_highlight
89
+ @original_element_colors.each_pair do |element, color|
90
+ perform_highlight do
91
+ element.style.backgroundColor = color
89
92
  end
90
- sleep 0.05
91
93
  end
94
+ @original_element_colors.clear
92
95
  end
93
96
 
94
97
  end # class Form
@@ -1,7 +1,9 @@
1
1
  module Watir
2
+ # Returned by the {Container#frame}.
2
3
  class Frame < Element
3
4
  include PageContainer
4
- attr_accessor :document
5
+
6
+ attr_writer :document
5
7
 
6
8
  attr_ole :name
7
9
  attr_ole :src
@@ -11,7 +13,26 @@ module Watir
11
13
  copy_test_config container
12
14
  end
13
15
 
16
+ # @return [WIN32OLE] frame's document object.
17
+ # @raise [FrameAccessDeniedException] when IE security settings won't allow
18
+ # to access the frame
19
+ # @macro exists
20
+ def document
21
+ assert_exists
22
+ if @document
23
+ @document
24
+ else
25
+ raise FrameAccessDeniedException, "IE will not allow access to this frame for security reasons. You can work around this with ie.goto(frame.src)"
26
+ end
27
+ end
28
+
29
+ # @private
30
+ def attach_command
31
+ @container.page_container.attach_command + ".frame(:unique_number => #{unique_number})"
32
+ end
33
+
14
34
  # Find the frame denoted by specifiers in the container and return its ole_object
35
+ # @private
15
36
  def locate
16
37
  frame, document = @container.locator_for(FrameLocator, @specifiers, self.class).locate
17
38
  if frame && document
@@ -26,22 +47,10 @@ module Watir
26
47
  end
27
48
  end
28
49
 
50
+ # @private
29
51
  def __ole_inner_elements
30
52
  document.body.all
31
53
  end
32
54
 
33
- def document
34
- assert_exists
35
- if @document
36
- @document
37
- else
38
- raise FrameAccessDeniedException, "IE will not allow access to this frame for security reasons. You can work around this with ie.goto(frame.src)"
39
- end
40
- end
41
-
42
- def attach_command
43
- @container.page_container.attach_command + ".frame(#{@specifiers.inspect})".gsub('"','\'')
44
- end
45
-
46
55
  end
47
56
  end
@@ -1,352 +1,332 @@
1
1
  module Watir
2
+ # Main browser class.
2
3
  class IE
3
4
  include WaitHelper
4
5
  include Exception
5
6
  include Container
6
7
  include PageContainer
7
8
 
8
- # Maximum number of seconds to wait when attaching to a window
9
- @@attach_timeout = 2.0 # default value
10
- def self.attach_timeout
11
- @@attach_timeout
12
- end
13
- def self.attach_timeout=(timeout)
14
- @@attach_timeout = timeout
15
- end
9
+ class << self
10
+ # Maximum number of seconds to wait when attaching to a window
11
+ attr_writer :attach_timeout
16
12
 
17
- # Return the options used when creating new instances of IE.
18
- # BUG: this interface invites misunderstanding/misuse such as IE.options[:speed] = :zippy]
19
- def self.options
20
- {:speed => self.speed, :visible => self.visible, :attach_timeout => self.attach_timeout, :zero_based_indexing => self.zero_based_indexing}
21
- end
22
- # set values for options used when creating new instances of IE.
23
- def self.set_options options
24
- options.each do |name, value|
25
- send "#{name}=", value
13
+ def attach_timeout
14
+ @attach_timeout ||= 2
26
15
  end
27
- end
28
- # The globals $FAST_SPEED and $HIDE_IE are checked both at initialization
29
- # and later, because they
30
- # might be set after initialization. Setting them beforehand (e.g. from
31
- # the command line) will affect the class, otherwise it is only a temporary
32
- # effect
33
- @@speed = $FAST_SPEED ? :fast : :slow
34
- def self.speed
35
- return :fast if $FAST_SPEED
36
- @@speed
37
- end
38
- def self.speed= x
39
- $FAST_SPEED = nil
40
- @@speed = x
41
- end
42
- @@visible = $HIDE_IE ? false : true
43
- def self.visible
44
- return false if $HIDE_IE
45
- @@visible
46
- end
47
- def self.visible= x
48
- $HIDE_IE = nil
49
- @@visible = x
50
- end
51
16
 
52
- @@zero_based_indexing = true
53
- def self.zero_based_indexing= enabled
54
- @@zero_based_indexing = enabled
55
- end
17
+ # Return the options used when creating new instances of IE.
18
+ # BUG: this interface invites misunderstanding/misuse such as IE.options[:speed] = :zippy]
19
+ def options
20
+ {:speed => self.speed, :visible => self.visible, :attach_timeout => self.attach_timeout}
21
+ end
56
22
 
57
- def self.zero_based_indexing
58
- @@zero_based_indexing
59
- end
23
+ # set values for options used when creating new instances of IE.
24
+ def set_options options
25
+ options.each do |name, value|
26
+ send "#{name}=", value
27
+ end
28
+ end
60
29
 
61
- def self.base_index
62
- self.zero_based_indexing ? 0 : 1
63
- end
30
+ # The speed in which browser will type keys etc. Possible values are
31
+ # :slow (default), :fast and :zippy.
32
+ attr_writer :speed
64
33
 
65
- # Used internally to determine when IE has finished loading a page
66
- READYSTATES = {:complete => 4}
34
+ def speed
35
+ @speed ||= :slow
36
+ end
67
37
 
68
- # The default color for highlighting objects as they are accessed.
69
- HIGHLIGHT_COLOR = 'yellow'
38
+ # Set browser window to visible or hidden. Defaults to true.
39
+ attr_writer :visible
70
40
 
71
- # The time, in seconds, it took for the new page to load after executing the
72
- # the last command
73
- attr_reader :down_load_time
41
+ def visible
42
+ @visible ||= true
43
+ end
74
44
 
75
- # the OLE Internet Explorer object
76
- attr_accessor :ie
45
+ # Create a new IE window.
46
+ def new_window
47
+ ie = new true
48
+ ie._new_window_init
49
+ ie
50
+ end
77
51
 
78
- # this contains the list of unique urls that have been visited
79
- attr_reader :url_list
52
+ # Create a new IE, starting at the specified url.
53
+ # @param [String] url url to navigate to.
54
+ def start(url=nil)
55
+ start_window url
56
+ end
80
57
 
81
- # Create a new IE window. Works just like IE.new in Watir 1.4.
82
- def self.new_window
83
- ie = new true
84
- ie._new_window_init
85
- ie
86
- end
58
+ # Create a new IE window, starting at the specified url.
59
+ # @param [String] url url to navigate to.
60
+ def start_window(url=nil)
61
+ ie = new_window
62
+ ie.goto url if url
63
+ ie
64
+ end
87
65
 
88
- # Create an IE browser.
89
- def initialize suppress_new_window=nil
90
- _new_window_init unless suppress_new_window
91
- end
66
+ # Create a new IE window in a new process.
67
+ # @note This method will not work when
68
+ # Watir/Ruby is run under a service (instead of a user).
69
+ def new_process
70
+ ie = new true
71
+ ie._new_process_init
72
+ ie
73
+ end
92
74
 
93
- def _new_window_init
94
- create_browser_window
95
- initialize_options
96
- goto 'about:blank' # this avoids numerous problems caused by lack of a document
97
- end
75
+ # Create a new IE window in a new process, starting at the specified URL.
76
+ # @param [String] url url to navigate to.
77
+ def start_process(url=nil)
78
+ ie = new_process
79
+ ie.goto url if url
80
+ ie
81
+ end
98
82
 
99
- # Create a new IE Window, starting at the specified url.
100
- # If no url is given, start empty.
101
- def self.start url=nil
102
- start_window url
103
- end
83
+ # Attach to an existing IE {Browser}.
84
+ #
85
+ # @example Attach with full title:
86
+ # Watir::Browser.attach(:title, "Full title of IE")
87
+ #
88
+ # @example Attach with part of the title using {Regexp}:
89
+ # Watir::Browser.attach(:title, /part of the title of IE/)
90
+ #
91
+ # @example Attach with part of the url:
92
+ # Watir::Browser.attach(:url, /google/)
93
+ #
94
+ # @example Attach with window handle:
95
+ # Watir::Browser.attach(:hwnd, 123456)
96
+ #
97
+ # @param [Symbol] how type of the locator. Can be :title, :url or :hwnd.
98
+ # @param [Symbol] what value of the locator. Can be {String}, {Regexp} or {Fixnum}
99
+ # depending of the type parameter.
100
+ #
101
+ # @note This method will not work when
102
+ # Watir/Ruby is run under a service (instead of a user).
103
+ def attach(how, what)
104
+ ie = new true # don't create window
105
+ ie._attach_init(how, what)
106
+ ie
107
+ end
104
108
 
105
- # Create a new IE window, starting at the specified url.
106
- # If no url is given, start empty. Works like IE.start in Watir 1.4.
107
- def self.start_window url=nil
108
- ie = new_window
109
- ie.goto url if url
110
- ie
111
- end
109
+ # Yields successively to each IE window on the current desktop. Takes a block.
110
+ # @note This method will not work when
111
+ # Watir/Ruby is run under a service (instead of a user).
112
+ # @yieldparam [IE] ie instances of IE found.
113
+ def each
114
+ shell = WIN32OLE.new('Shell.Application')
115
+ ie_browsers = []
116
+ shell.Windows.each do |window|
117
+ next unless (window.path =~ /Internet Explorer/ rescue false)
118
+ next unless (hwnd = window.hwnd rescue false)
119
+ ie = bind(window)
120
+ ie.hwnd = hwnd
121
+ ie_browsers << ie
122
+ end
123
+ ie_browsers.each do |ie|
124
+ yield ie
125
+ end
126
+ end
112
127
 
113
- # Create a new IE window in a new process.
114
- # This method will not work when
115
- # Watir/Ruby is run under a service (instead of a user).
116
- def self.new_process
117
- ie = new true
118
- ie._new_process_init
119
- ie
120
- end
128
+ # @return [String] the IE browser version number as a string.
129
+ def version
130
+ @ie_version ||= begin
131
+ require 'win32/registry'
132
+ ::Win32::Registry::HKEY_LOCAL_MACHINE.open("SOFTWARE\\Microsoft\\Internet Explorer") do |ie_key|
133
+ ie_key.read('Version').last
134
+ end
135
+ # OR: ::WIN32OLE.new("WScript.Shell").RegRead("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Internet Explorer\\Version")
136
+ end
137
+ end
121
138
 
122
- def _new_process_init
123
- iep = Process.start
124
- @ie = iep.window
125
- @process_id = iep.process_id
126
- initialize_options
127
- goto 'about:blank'
128
- end
139
+ # @return [Array<String>] the IE browser version numbers split by "." in an Array.
140
+ def version_parts
141
+ version.split('.')
142
+ end
129
143
 
130
- # Create a new IE window in a new process, starting at the specified URL.
131
- # Same as IE.start.
132
- def self.start_process url=nil
133
- ie = new_process
134
- ie.goto url if url
135
- ie
136
- end
144
+ # Find existing IE window with locators.
145
+ # @see .attach
146
+ def find(how, what)
147
+ ie_ole = _find(how, what)
148
+ bind ie_ole if ie_ole
149
+ end
137
150
 
138
- # Return a Watir::IE object for an existing IE window. Window can be
139
- # referenced by url, title, or window handle.
140
- # Second argument can be either a string or a regular expression in the
141
- # case of of :url or :title.
142
- # IE.attach(:url, 'http://www.google.com')
143
- # IE.attach(:title, 'Google')
144
- # IE.attach(:hwnd, 528140)
145
- # This method will not work when
146
- # Watir/Ruby is run under a service (instead of a user).
147
- def self.attach how, what
148
- ie = new true # don't create window
149
- ie._attach_init(how, what)
150
- ie
151
- end
151
+ # Return an IE object that wraps the given window, typically obtained from
152
+ # Shell.Application.windows.
153
+ # @private
154
+ def bind(window)
155
+ ie = new true
156
+ ie.ie = window
157
+ ie.initialize_options
158
+ ie
159
+ end
152
160
 
153
- # this method is used internally to attach to an existing window
154
- def _attach_init how, what
155
- attach_browser_window how, what
156
- initialize_options
157
- wait
158
- end
161
+ # @private
162
+ def _find(how, what)
163
+ _find_all(how, what).first
164
+ end
165
+
166
+ # @private
167
+ def _find_all(how, what)
168
+ ies = []
169
+ count = -1
170
+ each do |ie|
171
+ window = ie.ie
172
+
173
+ case how
174
+ when :url
175
+ ies << window if (what.matches(window.locationURL))
176
+ when :title
177
+ # normal windows explorer shells do not have document
178
+ # note window.document will fail for "new" browsers
179
+ begin
180
+ title = window.locationname
181
+ title = window.document.title
182
+ rescue WIN32OLERuntimeError
183
+ end
184
+ ies << window if what.matches(title)
185
+ when :hwnd
186
+ begin
187
+ ies << window if what == window.HWND
188
+ rescue WIN32OLERuntimeError
189
+ end
190
+ when :index
191
+ count += 1
192
+ if count == what
193
+ ies << window
194
+ break
195
+ end
196
+ when nil
197
+ ies << window
198
+ else
199
+ raise ArgumentError
200
+ end
201
+ end
202
+
203
+ ies
204
+ end
159
205
 
160
- # Return an IE object that wraps the given window, typically obtained from
161
- # Shell.Application.windows.
162
- def self.bind window
163
- ie = new true
164
- ie.ie = window
165
- ie.initialize_options
166
- ie
167
206
  end
168
207
 
169
- def initialize_options
170
- self.visible = IE.visible
171
- self.speed = IE.speed
208
+ # Used internally to determine when IE has finished loading a page.
209
+ # @private
210
+ READYSTATES = {:complete => 4}
172
211
 
173
- @ole_object = nil
174
- @page_container = self
175
- @error_checkers = []
176
- @activeObjectHighLightColor = HIGHLIGHT_COLOR
177
- @url_list = []
212
+ # The default color for highlighting objects as they are accessed.
213
+ # @private
214
+ HIGHLIGHT_COLOR = 'yellow'
215
+
216
+ # The time, in seconds, it took for the new page to load after executing
217
+ # the last command.
218
+ attr_reader :down_load_time
219
+
220
+ # The OLE Internet Explorer object.
221
+ attr_accessor :ie
222
+
223
+ # The list of unique urls that have been visited.
224
+ attr_reader :url_list
225
+
226
+ # @private
227
+ attr_writer :hwnd
228
+
229
+ # Create an IE browser instance.
230
+ # @param [Boolean] suppress_new_window set to true for not creating a IE
231
+ # window.
232
+ def initialize(suppress_new_window=nil)
233
+ _new_window_init unless suppress_new_window
178
234
  end
179
235
 
180
- # Specifies the speed that commands will be executed at. Choices are:
236
+ # Specifies the speed that commands will be executed at.
237
+ # Possible choices are:
181
238
  # * :slow (default)
182
239
  # * :fast
183
240
  # * :zippy
184
- # With IE#speed= :zippy, text fields will be entered at once, instead of
185
- # character by character (default).
186
- def speed= how_fast
241
+ #
242
+ # With :zippy, text fields will be entered at once, instead of
243
+ # character by character.
244
+ #
245
+ # @note :zippy speed does not trigger JavaScript events like onChange etc.
246
+ #
247
+ # @param [Symbol] how_fast possible choices are :slow (default), :fast and
248
+ # :zippy
249
+ # @raise [ArgumentError] when invalid speed is specified.
250
+ def speed=(how_fast)
187
251
  case how_fast
188
- when :zippy then
252
+ when :zippy
189
253
  @typingspeed = 0
190
254
  @pause_after_wait = 0.01
191
255
  @type_keys = false
192
256
  @speed = :fast
193
- when :fast then
257
+ when :fast
194
258
  @typingspeed = 0
195
259
  @pause_after_wait = 0.01
196
260
  @type_keys = true
197
261
  @speed = :fast
198
- when :slow then
262
+ when :slow
199
263
  @typingspeed = 0.08
200
264
  @pause_after_wait = 0.1
201
265
  @type_keys = true
202
266
  @speed = :slow
203
267
  else
204
- raise ArgumentError, "Invalid speed: #{how_fast}"
268
+ raise ArgumentError, "Invalid speed: #{how_fast}. Possible choices are :slow, :fast and :zippy."
205
269
  end
206
270
  end
207
271
 
272
+ # @return [Symbol] current speed setting. May be :slow, :fast or :zippy.
208
273
  def speed
209
274
  return @speed if @speed == :slow
210
275
  return @type_keys ? :fast : :zippy
211
276
  end
212
277
 
213
- # deprecated: use speed = :fast instead
278
+ # @deprecated Use {#speed=} with :fast argument instead.
214
279
  def set_fast_speed
280
+ Kernel.warn "Deprecated(IE.set_fast_speed) - use Browser#speed = :fast instead."
215
281
  self.speed = :fast
216
282
  end
217
283
 
218
- # deprecated: use speed = :slow instead
284
+ # @deprecated Use {#speed=} with :slow argument instead.
219
285
  def set_slow_speed
286
+ Kernel.warn "Deprecated(IE.set_slow_speed) - use Browser#speed = :slow instead."
220
287
  self.speed = :slow
221
288
  end
222
289
 
290
+ # @return [Boolean] true when window is visible, false otherwise.
223
291
  def visible
224
292
  @ie.visible
225
293
  end
294
+
295
+ # Set the visibility of IE window.
296
+ # @param [Boolean] boolean set to true if IE window should be visible, false
297
+ # otherwise.
226
298
  def visible=(boolean)
227
299
  @ie.visible = boolean if boolean != @ie.visible
228
300
  end
229
301
 
230
- # Yields successively to each IE window on the current desktop. Takes a block.
231
- # This method will not work when
232
- # Watir/Ruby is run under a service (instead of a user).
233
- # Yields to the window and its hwnd.
234
- def self.each
235
- shell = WIN32OLE.new('Shell.Application')
236
- ie_browsers = []
237
- shell.Windows.each do |window|
238
- next unless (window.path =~ /Internet Explorer/ rescue false)
239
- next unless (hwnd = window.hwnd rescue false)
240
- ie = IE.bind(window)
241
- ie.hwnd = hwnd
242
- ie_browsers << ie
243
- end
244
- ie_browsers.each do |ie|
245
- yield ie
246
- end
247
- end
248
-
249
- def self.version
250
- @ie_version ||= begin
251
- require 'win32/registry'
252
- ::Win32::Registry::HKEY_LOCAL_MACHINE.open("SOFTWARE\\Microsoft\\Internet Explorer") do |ie_key|
253
- ie_key.read('Version').last
254
- end
255
- # OR: ::WIN32OLE.new("WScript.Shell").RegRead("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Internet Explorer\\Version")
256
- end
257
- end
258
-
259
- def self.version_parts
260
- version.split('.')
261
- end
262
-
263
- # return internet explorer instance as specified. if none is found,
264
- # return nil.
265
- # arguments:
266
- # :url, url -- the URL of the IE browser window
267
- # :title, title -- the title of the browser page
268
- # :hwnd, hwnd -- the window handle of the browser window.
269
- # This method will not work when
270
- # Watir/Ruby is run under a service (instead of a user).
271
- def self.find(how, what)
272
- ie_ole = IE._find(how, what)
273
- IE.bind ie_ole if ie_ole
274
- end
275
-
276
- def self._find(how, what)
277
- self._find_all(how, what).first
278
- end
279
-
280
- def self._find_all(how, what)
281
- ies = []
282
- count = -1
283
- IE.each do |ie|
284
- window = ie.ie
285
-
286
- case how
287
- when :url
288
- ies << window if (what.matches(window.locationURL))
289
- when :title
290
- # normal windows explorer shells do not have document
291
- # note window.document will fail for "new" browsers
292
- begin
293
- title = window.locationname
294
- title = window.document.title
295
- rescue WIN32OLERuntimeError
296
- end
297
- ies << window if what.matches(title)
298
- when :hwnd
299
- begin
300
- ies << window if what == window.HWND
301
- rescue WIN32OLERuntimeError
302
- end
303
- when :index
304
- count += 1
305
- if count == what
306
- ies << window
307
- break
308
- end
309
- when nil
310
- ies << window
311
- else
312
- raise ArgumentError
313
- end
314
- end
315
-
316
- ies
317
- end
318
-
319
- # Return the current window handle
302
+ # @return [Fixnum] current IE window handle.
303
+ # @raise [RuntimeError] when not attached to a browser.
320
304
  def hwnd
321
305
  raise "Not attached to a browser" if @ie.nil?
322
306
  @hwnd ||= @ie.hwnd
323
307
  end
324
- attr_writer :hwnd
325
308
 
309
+ # @return [Symbol] the name of the browser. Is always :ie.
326
310
  def name
327
311
  :ie
328
312
  end
329
313
 
330
- # Are we attached to an open browser?
314
+ # @return [Boolean] true when IE is window exists, false otherwise.
331
315
  def exists?
332
- begin
333
- !!(@ie.name =~ /Internet Explorer/)
334
- rescue WIN32OLERuntimeError, NoMethodError
335
- false
336
- end
316
+ !!(@ie.name =~ /Internet Explorer/)
317
+ rescue WIN32OLERuntimeError, NoMethodError
318
+ false
337
319
  end
338
- alias :exist? :exists?
339
320
 
340
- #
341
- # Accessing data outside the document
342
- #
321
+ alias :exist? :exists?
343
322
 
344
- # Return the title of the document
323
+ # @return [String] the title of the document.
345
324
  def title
346
325
  @ie.document.title
347
326
  end
348
327
 
349
- # Return the status of the window, typically from the status bar at the bottom.
328
+ # @return [String] the status text of the window, typically from the status bar at the bottom.
329
+ # Will be empty if there's no status or when there are problems accessing status text.
350
330
  def status
351
331
  @ie.statusText
352
332
  rescue WIN32OLERuntimeError
@@ -358,7 +338,8 @@ module Watir
358
338
  #
359
339
 
360
340
  # Navigate to the specified URL.
361
- # * url - string - the URL to navigate to
341
+ # @param [String] url url to navigate to.
342
+ # @return [Fixnum] time in seconds the page took to load.
362
343
  def goto(url)
363
344
  url = "http://" + url unless url =~ %r{://} || url == "about:blank"
364
345
  @ie.navigate(url)
@@ -366,22 +347,22 @@ module Watir
366
347
  return @down_load_time
367
348
  end
368
349
 
369
- # Go to the previous page - the same as clicking the browsers back button
370
- # an WIN32OLERuntimeError exception is raised if the browser cant go back
350
+ # Go to the previous page - the same as clicking the browsers back button.
351
+ # @raise [WIN32OLERuntimeError] when the browser can't go back.
371
352
  def back
372
353
  @ie.GoBack
373
354
  wait
374
355
  end
375
356
 
376
- # Go to the next page - the same as clicking the browsers forward button
377
- # an WIN32OLERuntimeError exception is raised if the browser cant go forward
357
+ # Go to the next page - the same as clicking the browsers forward button.
358
+ # @raise [WIN32OLERuntimeError] when the browser can't go forward.
378
359
  def forward
379
360
  @ie.GoForward
380
361
  wait
381
362
  end
382
363
 
383
- # Refresh the current page - the same as clicking the browsers refresh button
384
- # an WIN32OLERuntimeError exception is raised if the browser cant refresh
364
+ # Refresh the current page - the same as clicking the browsers refresh button.
365
+ # @raise [WIN32OLERuntimeError] when the browser can't refresh.
385
366
  def refresh
386
367
  @ie.refresh2(3)
387
368
  wait
@@ -391,12 +372,12 @@ module Watir
391
372
  '#<%s:0x%x url=%s title=%s>' % [self.class, hash*2, url.inspect, title.inspect]
392
373
  end
393
374
 
394
- # clear the list of urls that we have visited
375
+ # Clear the list of urls that have been visited.
395
376
  def clear_url_list
396
377
  @url_list.clear
397
378
  end
398
379
 
399
- # Closes the Browser
380
+ # Close the {Browser}.
400
381
  def close
401
382
  return unless exists?
402
383
  @ie.stop
@@ -412,95 +393,181 @@ module Watir
412
393
  end
413
394
  end
414
395
 
415
- # Maximize the window (expands to fill the screen)
396
+ # Maximize the window (expands to fill the screen).
416
397
  def maximize
417
398
  rautomation.maximize
418
399
  end
419
400
 
420
- # Minimize the window (appears as icon on taskbar)
401
+ # Minimize the window (appears as icon on taskbar).
421
402
  def minimize
422
403
  rautomation.minimize
423
404
  end
424
405
 
406
+ # @return [Boolean] true when window is minimized, false otherwise.
425
407
  def minimized?
426
408
  rautomation.minimized?
427
409
  end
428
410
 
429
- # Restore the window (after minimizing or maximizing)
411
+ # Restore the window (after minimizing or maximizing).
430
412
  def restore
431
413
  rautomation.restore
432
414
  end
433
415
 
434
- # Make the window come to the front
416
+ # Make the window come to the front.
435
417
  def activate
436
418
  rautomation.activate
437
419
  end
420
+
438
421
  alias :bring_to_front :activate
439
422
 
423
+ # @return [Boolean] true when window is in front e.g. in focus, false otherwise.
440
424
  def active?
441
425
  rautomation.active?
442
426
  end
427
+
443
428
  alias :front? :active?
444
429
 
430
+ # @return [RAutomation::Window] the RAutomation instance for this IE window.
431
+ # @see https://github.com/jarmo/rautomation
445
432
  def rautomation
446
433
  @rautomation ||= ::RAutomation::Window.new(:hwnd => hwnd)
447
- @rautomation
448
434
  end
449
435
 
436
+ # @deprecated use {#rautomation} instead.
450
437
  def autoit
451
- Kernel.warn "Usage of Watir::IE#autoit method is DEPRECATED! Use Watir::IE#rautomation method instead. Refer to https://github.com/jarmo/RAutomation for updating your scripts."
438
+ Kernel.warn "Deprecated(IE#autoit) - use IE#rautomation instead. Refer to https://github.com/jarmo/RAutomation for updating your scripts."
452
439
  @autoit ||= ::RAutomation::Window.new(:hwnd => hwnd, :adapter => :autoit)
453
- @autoit
454
440
  end
455
441
 
456
442
  # Activates the window and sends keys to it.
457
443
  #
458
- # Example:
459
- # browser.send_keys("Hello World{enter}")
444
+ # @example
445
+ # browser.send_keys("Hello World", :enter)
460
446
  #
461
- # Refer to RAutomation::Adapter::WinFfi::KeystrokeConverter.convert_special_characters for
462
- # special characters conversion.
463
- # @see RAutomation::Window#send_keys
447
+ # @see https://github.com/jarmo/RAutomation/blob/master/lib/rautomation/adapter/win_32/window.rb RAutomation::Window#send_keys documentation.
464
448
  def send_keys(*keys)
465
449
  rautomation.send_keys *keys
466
450
  end
467
451
 
468
- def dir
469
- return File.expand_path(File.dirname(__FILE__))
470
- end
471
-
472
452
  #
473
453
  # Document and Document Data
474
454
  #
475
455
 
476
- # Return the current document
456
+ # @return [WIN32OLE] current IE document.
477
457
  def document
478
- return @ie.document
458
+ @ie.document
479
459
  end
480
460
 
481
- # returns the current url, as displayed in the address bar of the browser
461
+ # @return [String] current url, as displayed in the address bar of the browser.
482
462
  def url
483
- return @ie.LocationURL
463
+ @ie.LocationURL
484
464
  end
485
465
 
466
+ # Create a {Screenshot} instance.
486
467
  def screenshot
487
468
  Screenshot.new(hwnd)
488
469
  end
489
470
 
471
+ # Retrieve a {Window} instance.
472
+ #
473
+ # @example Retrieve a different window without block.
474
+ # browser.window(:title => /other window title/).use
475
+ # browser.title # => "other window title"
476
+ #
477
+ # @example Use different window with block.
478
+ # browser.window(:title => /other window title/) do
479
+ # browser.title # => "other window title"
480
+ # end
481
+ # browser.title # => "current window title"
482
+ #
483
+ # @param [Hash] specifiers options for finding window.
484
+ # @option specifiers [String,Regexp] :title Title of the window.
485
+ # @option specifiers [String,Regexp] :url Url of the window.
486
+ # @option specifiers [Fixnum] :index The index of the window.
487
+ # @yield yield optionally to the found window.
488
+ # @return [Window] found window instance.
490
489
  def window(specifiers={}, &blk)
491
490
  win = Window.new(self, specifiers, &blk)
492
491
  win.use &blk if blk
493
492
  win
494
493
  end
495
494
 
496
- def windows(specifiers={}, &blk)
497
- self.class._find_all(specifiers.keys.first, specifiers.values.first).map {|ie| Window.new(self, specifiers, IE.bind(ie), &blk)}
495
+ # @see #window
496
+ # @return [Array<Window>] array of found windows.
497
+ def windows(specifiers={})
498
+ self.class._find_all(specifiers.keys.first, specifiers.values.first).map {|ie| Window.new(self, specifiers, IE.bind(ie))}
498
499
  end
499
500
 
501
+ # Retrieve {Cookies} instance.
500
502
  def cookies
501
503
  Cookies.new(self)
502
504
  end
503
505
 
506
+ # Add an error checker that gets executed after every page load, click etc.
507
+ #
508
+ # @example
509
+ # browser.add_checker lambda { |browser| raise "Error!" if browser.text.include? "Error" }
510
+ #
511
+ # @param [Proc] checker Proc object which gets yielded with {IE} instance.
512
+ def add_checker(checker)
513
+ @error_checkers << checker
514
+ end
515
+
516
+ # Disable an error checker added via {#add_checker}.
517
+ #
518
+ # @param [Proc] checker Proc object to be removed from error checkers.
519
+ def disable_checker(checker)
520
+ @error_checkers.delete(checker)
521
+ end
522
+
523
+ # Gives focus to the window frame.
524
+ def focus
525
+ active_element = document.activeElement
526
+ active_element.blur unless active_element.tagName == "BODY"
527
+ document.focus
528
+ end
529
+
530
+ # @private
531
+ def attach_command
532
+ "Watir::IE.attach(:hwnd, #{hwnd})"
533
+ end
534
+
535
+ # @private
536
+ def _new_window_init
537
+ create_browser_window
538
+ initialize_options
539
+ goto 'about:blank' # this avoids numerous problems caused by lack of a document
540
+ end
541
+
542
+ # @private
543
+ def _new_process_init
544
+ iep = Process.start
545
+ @ie = iep.window
546
+ @process_id = iep.process_id
547
+ initialize_options
548
+ goto 'about:blank'
549
+ end
550
+
551
+ # this method is used internally to attach to an existing window
552
+ # @private
553
+ def _attach_init how, what
554
+ attach_browser_window how, what
555
+ initialize_options
556
+ wait
557
+ end
558
+
559
+ # @private
560
+ def initialize_options
561
+ self.visible = IE.visible
562
+ self.speed = IE.speed
563
+
564
+ @ole_object = nil
565
+ @page_container = self
566
+ @error_checkers = []
567
+ @active_object_highlight_color = HIGHLIGHT_COLOR
568
+ @url_list = []
569
+ end
570
+
504
571
  #
505
572
  # Synchronization
506
573
  #
@@ -508,9 +575,10 @@ module Watir
508
575
  # Block execution until the page has loaded.
509
576
  #
510
577
  # Will raise Timeout::Error if page hasn't been loaded within 5 minutes.
511
- # =nodoc
512
578
  # Note: This code needs to be prepared for the ie object to be closed at
513
579
  # any moment!
580
+ #
581
+ # @private
514
582
  def wait(no_sleep=false)
515
583
  @xml_parser_doc = nil
516
584
  @down_load_time = 0.0
@@ -562,34 +630,13 @@ module Watir
562
630
 
563
631
  # Error checkers
564
632
 
565
- # this method runs the predefined error checks
633
+ # Run the predefined error checks.
634
+ #
635
+ # @private
566
636
  def run_error_checks
567
637
  @error_checkers.each { |e| e.call(self) }
568
638
  end
569
639
 
570
- # this method is used to add an error checker that gets executed on every page load
571
- # * checker Proc Object, that contains the code to be run
572
- def add_checker(checker)
573
- @error_checkers << checker
574
- end
575
-
576
- # this allows a checker to be disabled
577
- # * checker Proc Object, the checker that is to be disabled
578
- def disable_checker(checker)
579
- @error_checkers.delete(checker)
580
- end
581
-
582
- # Gives focus to the frame
583
- def focus
584
- active_element = document.activeElement
585
- active_element.blur unless active_element.tagName == "BODY"
586
- document.focus
587
- end
588
-
589
- def attach_command
590
- "Watir::IE.attach(:hwnd, #{hwnd})"
591
- end
592
-
593
640
  private
594
641
 
595
642
  def create_browser_window
@@ -609,5 +656,6 @@ module Watir
609
656
  @ie = ieTemp
610
657
  end
611
658
 
659
+
612
660
  end # class IE
613
661
  end