watir-classic 3.3.0 → 3.4.0

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