Project

General

Profile

Bug #1140 ยป element.rb

where it parses wrong - simian (Ben Rayfield), 02/11/2009 05:00 AM

 
1
module Watir
2
  # Base class for html elements.
3
  # This is not a class that users would normally access.
4
  class Element # Wrapper
5
    include Watir::Exception
6
    include Container # presumes @container is defined
7
    attr_accessor :container
8
    
9
    # number of spaces that separate the property from the value in the to_s method
10
    TO_S_SIZE = 14
11
    
12
    # ole_object - the ole object for the element being wrapped
13
    def initialize(ole_object)
14
      @o = ole_object
15
      @original_color = nil
16
    end
17
    
18
    # Return the ole object, allowing any methods of the DOM that Watir doesn't support to be used.
19
    def ole_object # BUG: should use an attribute reader and rename the instance variable
20
      return @o
21
    end
22
    def ole_object=(o)
23
      @o = o
24
    end
25
    
26
    private
27
    def self.def_wrap(ruby_method_name, ole_method_name=nil)
28
      ole_method_name = ruby_method_name unless ole_method_name
29
      class_eval "def #{ruby_method_name}
30
                          assert_exists
31
                          ole_object.invoke('#{ole_method_name}')
32
                        end"
33
    end
34
    def self.def_wrap_guard(method_name)
35
      class_eval "def #{method_name}
36
                          assert_exists
37
                          begin
38
                            ole_object.invoke('#{method_name}')
39
                          rescue
40
                            ''
41
                          end
42
                        end"
43
    end
44

    
45
    public
46
    def assert_exists
47
      locate if defined?(locate)
48
      unless ole_object
49
        raise UnknownObjectException.new("Unable to locate object, using #{@how} and #{@what}")
50
      end
51
    end
52
    def assert_enabled
53
      unless enabled?
54
        raise ObjectDisabledException, "object #{@how} and #{@what} is disabled"
55
      end
56
    end
57
    
58
    # return the name of the element (as defined in html)
59
    def_wrap_guard :name
60
    # return the id of the element
61
    def_wrap_guard :id
62
    # return whether the element is disabled
63
    def_wrap :disabled
64
    alias disabled? disabled
65
    # return the value of the element
66
    def_wrap_guard :value
67
    # return the title of the element
68
    def_wrap_guard :title
69
    # return the style of the element
70
    def_wrap_guard :style
71
    
72
    def_wrap_guard :alt
73
    def_wrap_guard :src
74
    
75
    # return the type of the element
76
    def_wrap_guard :type # input elements only
77
    # return the url the link points to
78
    def_wrap :href # link only
79
    # return the ID of the control that this label is associated with
80
    def_wrap :for, :htmlFor # label only
81
    # return the class name of the element
82
    # raise an ObjectNotFound exception if the object cannot be found
83
    def_wrap :class_name, :className
84
    # return the unique COM number for the element
85
    def_wrap :unique_number, :uniqueNumber
86
    # Return the outer html of the object - see http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/outerhtml.asp?frame=true
87
    def_wrap :html, :outerHTML
88

    
89
    # return the text before the element
90
    def before_text # label only
91
      assert_exists
92
      begin
93
        ole_object.getAdjacentText("afterEnd").strip
94
      rescue
95
                ''
96
      end
97
    end
98
    
99
    # return the text after the element
100
    def after_text # label only
101
      assert_exists
102
      begin
103
        ole_object.getAdjacentText("beforeBegin").strip
104
      rescue
105
                ''
106
      end
107
    end
108
    
109
    # Return the innerText of the object
110
    # Raise an ObjectNotFound exception if the object cannot be found
111
    def text
112
      assert_exists
113
      return ole_object.innerText.strip
114
    end
115
    
116
    def ole_inner_elements
117
      assert_exists
118
      return ole_object.all
119
    end
120
    private :ole_inner_elements
121
    
122
    def document
123
      assert_exists
124
      return ole_object
125
    end
126

    
127
    # Return the element immediately containing self. 
128
    def parent
129
      assert_exists
130
      result = Element.new(ole_object.parentelement)
131
      result.set_container self
132
      result
133
    end
134
    
135
    include Comparable
136
    def <=> other
137
      assert_exists
138
      other.assert_exists
139
      ole_object.sourceindex <=> other.ole_object.sourceindex
140
    end
141

    
142
    # Return true if self is contained earlier in the html than other. 
143
    alias :before? :< 
144
    # Return true if self is contained later in the html than other. 
145
    alias :after? :> 
146
      
147
    def typingspeed
148
      @container.typingspeed
149
    end
150
    
151
    def activeObjectHighLightColor
152
      @container.activeObjectHighLightColor
153
    end
154
    
155
    # Return an array with many of the properties, in a format to be used by the to_s method
156
    def string_creator
157
      n = []
158
      n <<   "type:".ljust(TO_S_SIZE) + self.type
159
      n <<   "id:".ljust(TO_S_SIZE) +         self.id.to_s
160
      n <<   "name:".ljust(TO_S_SIZE) +       self.name.to_s
161
      n <<   "value:".ljust(TO_S_SIZE) +      self.value.to_s
162
      n <<   "disabled:".ljust(TO_S_SIZE) +   self.disabled.to_s
163
      return n
164
    end
165
    private :string_creator
166
    
167
    # Display basic details about the object. Sample output for a button is shown.
168
    # Raises UnknownObjectException if the object is not found.
169
    #      name      b4
170
    #      type      button
171
    #      id         b5
172
    #      value      Disabled Button
173
    #      disabled   true
174
    def to_s
175
      assert_exists
176
      return string_creator.join("\n")
177
    end
178
    
179
    # This method is responsible for setting and clearing the colored highlighting on the currently active element.
180
    # use :set   to set the highlight
181
    #   :clear  to clear the highlight
182
    # TODO: Make this two methods: set_highlight & clear_highlight
183
    # TODO: Remove begin/rescue blocks
184
    def highlight(set_or_clear)
185
      if set_or_clear == :set
186
        begin
187
          @original_color ||= style.backgroundColor
188
          style.backgroundColor = @container.activeObjectHighLightColor
189
        rescue
190
          @original_color = nil
191
        end
192
      else # BUG: assumes is :clear, but could actually be anything
193
        begin
194
          style.backgroundColor = @original_color unless @original_color == nil
195
        rescue
196
          # we could be here for a number of reasons...
197
          # e.g. page may have reloaded and the reference is no longer valid
198
        ensure
199
          @original_color = nil
200
        end
201
      end
202
    end
203
    private :highlight
204
    
205
    #   This method clicks the active element.
206
    #   raises: UnknownObjectException  if the object is not found
207
    #   ObjectDisabledException if the object is currently disabled
208
    def click
209
      click!
210
      @container.wait
211
    end
212
    
213
    def click_no_wait
214
      assert_enabled
215
      
216
      highlight(:set)
217
      object = "#{self.class}.new(self, :unique_number, #{self.unique_number})"
218
      @page_container.eval_in_spawned_process(object + ".click!")
219
      highlight(:clear)
220
    end
221

    
222
    def click!
223
      assert_enabled
224
      
225
      highlight(:set)
226
      ole_object.click
227
      highlight(:clear)
228
    end
229
    
230
    # Flash the element the specified number of times.
231
    # Defaults to 10 flashes.
232
    def flash number=10
233
      assert_exists
234
      number.times do
235
        highlight(:set)
236
        sleep 0.05
237
        highlight(:clear)
238
        sleep 0.05
239
      end
240
      nil
241
    end
242
    
243
    # Executes a user defined "fireEvent" for objects with JavaScript events tied to them such as DHTML menus.
244
    #   usage: allows a generic way to fire javascript events on page objects such as "onMouseOver", "onClick", etc.
245
    #   raises: UnknownObjectException  if the object is not found
246
    #           ObjectDisabledException if the object is currently disabled
247
    def fire_event(event)
248
      assert_enabled
249
      
250
      highlight(:set)
251
      ole_object.fireEvent(event)
252
      @container.wait
253
      highlight(:clear)
254
    end
255
    
256
    # This method sets focus on the active element.
257
    #   raises: UnknownObjectException  if the object is not found
258
    #           ObjectDisabledException if the object is currently disabled
259
    def focus
260
      assert_enabled
261
      ole_object.focus
262
    end
263
    
264
    # Returns whether this element actually exists.
265
    def exists?
266
      begin
267
        locate if defined?(locate)
268
      rescue WIN32OLERuntimeError
269
        @o = nil
270
      end
271
      @o ? true: false
272
    end
273
    alias :exist? :exists?
274
    
275
    # Returns true if the element is enabled, false if it isn't.
276
    #   raises: UnknownObjectException  if the object is not found
277
    def enabled?
278
      assert_exists
279
      return ! disabled
280
    end
281
    
282
    # Get attribute value for any attribute of the element.
283
    # Returns null if attribute doesn't exist.
284
    def attribute_value(attribute_name)
285
      assert_exists
286
      return ole_object.getAttribute(attribute_name)
287
    end
288
    
289
  end
290
  
291
  class ElementMapper # Still to be used
292
    include Container
293
    
294
    def initialize wrapper_class, container, how, what
295
      @wrapper_class = wrapper_class
296
      set_container
297
      @how = how
298
      @what = what
299
    end
300
    
301
    def method_missing method, *args
302
      locate
303
      @wrapper_class.new(@o).send(method, *args)
304
    end
305
  end
306
end