watir 2.0.4 → 3.0.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGES CHANGED
@@ -1,3 +1,44 @@
1
+ == 3.0.0 - 2012/01/13 - more conformance with WatirSpec
2
+
3
+ * Button#text returns value if exists instead of text
4
+ * Browser#goto prepends url automatically with http:// if scheme is missing
5
+ * Browser#element(s)_by_(xpath/css) are now private methods - use #element(:css => ...) and #element(:xpath => ...) instead
6
+ * Browser#label supports searching by :for attribute
7
+ * Browser#options method for searching <option> elements
8
+ * Browser#body, #thead, #tfoot, #tbody, #frameset and #fieldset added
9
+ * Browser#window and Browser#windows added implementing window switching API (https://github.com/jarib/watirspec/blob/master/window_switching_spec.rb)
10
+ * Element#present? returns false if exception is thrown by #exists? or #visible?
11
+ * Element#style returns CSS text instead of OLE object
12
+ * Element#text returns an empty string for non-visible elements
13
+ * Element#parent returns correct instance of Element class (e.g. Div instead of Element)
14
+ * Element#focus focuses document before focusing on the element
15
+ * Element#right_click and Element#double_click added
16
+ * Element#to_subtype added which returns specific instance of Watir::Element subclass
17
+ * Element#send_keys added
18
+ * Element#eql? as an alias for Element#== added
19
+ * Element#tag_name added
20
+ * ElementCollection#[] method supports negative indexes like regular Arrays
21
+ * ElementCollection#[] returns always an object, even if the index is out of bounds
22
+ * FileField#set and Image#save uses correct Windows file path (e.g. convert "\"-es into "/"-es)
23
+ * FileField#set raises Errno::ENOENT instead of WatirException if file doesn't exist
24
+ * FileField#value= as an alias for FileField#set added
25
+ * Font#color, #face and #size added
26
+ * Form#submit triggers onSubmit event and doesn't submit the form if event's callback returns false
27
+ * Frame#execute_script added
28
+ * Image#file_size, #height and #width return integer instead of a string
29
+ * Image#save blocking fixed
30
+ * Meta#content and #http_equiv added
31
+ * Option code rewritten, causing changes in its API
32
+ * SelectList code rewritten, causing changes in its API
33
+ * SelectList#(selected_)options returns now Options collection instead of an array of strings
34
+ * Table and its subelements code rewritten, causing changes in its API
35
+ * Table#strings and #hashes added
36
+ * TextField#label added
37
+ * searching elements will find only correct types - e.g. using Browser#div returns only DIV element even if all other provided selectors match
38
+ * all selectors and tag of the element needs to match even if searching by :id
39
+ * supporting html5 data-* attributes by using :data_* for locating and #data_* for retrieving attribute values
40
+ * many other internal changes
41
+
1
42
  == Version 2.0.4 - 2011/10/29
2
43
 
3
44
  * IE#execute_script escapes multi-line JavaScript scripts
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.4
1
+ 3.0.0.rc1
@@ -1,25 +1,13 @@
1
1
  module Watir
2
2
  class InputElementCollections < ElementCollections
3
3
  def each
4
- @container.input_element_locator(@how, @what, element_class::INPUT_TYPES, element_class).each {|element| yield element}
4
+ @container.locator_for(InputElementLocator, element_class::INPUT_TYPES, @how, @what, element_class).each {|element| yield element}
5
5
  end
6
6
  end
7
7
 
8
- class Frames < ElementCollections
9
- def each
10
- @container.locator_for(FrameLocator, @how, @what).each {|element| yield element}
11
- end
12
- end
13
-
14
- class Forms < ElementCollections
15
- def each
16
- @container.locator_for(FormLocator, @how, @what).each {|element| yield element}
17
- end
18
- end
19
-
20
8
  class HTMLElements < ElementCollections
21
9
  def each
22
- @container.locator_for(ElementLocator, @how, @what).each { |element| yield element }
10
+ @container.locator_for(TaggedElementLocator, ["*"], @how, @what, Element).each { |element| yield element }
23
11
  end
24
12
  end
25
13
 
@@ -44,10 +32,14 @@ module Watir
44
32
  class Inses < ElementCollections
45
33
  def element_class; Ins; end
46
34
  end
47
-
48
- %w[Link Li Map Area Image Table TableRow TableCell
35
+
36
+ class TableSectionCollection < ElementCollections
37
+ def element_class; TableSection; end
38
+ end
39
+
40
+ %w[Form Frame Link Li Map Area Image Table TableRow TableCell TableHeader TableFooter TableBody
49
41
  Label Pre P Span Div Dl Dt Dd Strong Em Del
50
- Font H1 H2 H3 H4 H5 H6 Meta Ol Ul].each do |element|
42
+ Font H1 H2 H3 H4 H5 H6 Meta Ol Ul FieldSet Option].each do |element|
51
43
  module_eval %Q{
52
44
  class #{element}s < ElementCollections; end
53
45
  }
@@ -98,20 +98,8 @@ module Watir
98
98
  # Not for external use, but cannot set to private due to usages in Element
99
99
  # classes.
100
100
 
101
- def input_element_locator(how, what, types, klass=nil)
102
- locator = InputElementLocator.new self, types, klass
103
- locator.set_specifier how, what
104
- locator
105
- end
106
-
107
- def tagged_element_locator(tag, how, what, klass=nil)
108
- locator = TaggedElementLocator.new self, tag, klass
109
- locator.set_specifier how, what
110
- locator
111
- end
112
-
113
- def locator_for(locator_class, how, what)
114
- locator = locator_class.new self
101
+ def locator_for(locator_class, tags, how, what, klass)
102
+ locator = locator_class.new self, tags, klass
115
103
  locator.set_specifier how, what
116
104
  locator
117
105
  end
@@ -27,6 +27,7 @@ require 'watir/table'
27
27
  require 'watir/image'
28
28
  require 'watir/link'
29
29
  require 'watir/html_element'
30
+ require 'watir/window'
30
31
 
31
32
  require 'watir/win32'
32
33
  require 'watir/modal_dialog'
@@ -8,12 +8,14 @@ module Watir
8
8
  assert_file_exists(file_path)
9
9
  assert_exists
10
10
  click_no_wait
11
- set_file_name file_path
11
+ set_file_name file_path.gsub(File::SEPARATOR, File::ALT_SEPARATOR)
12
12
  open_button.click
13
13
  end
14
14
 
15
+ alias_method :value=, :set
16
+
15
17
  def assert_file_exists(file_path)
16
- raise WatirException, "#{file_path} has to exist to set!" unless File.exists?(file_path)
18
+ raise Errno::ENOENT, "#{file_path} has to exist to set!" unless File.exists?(file_path)
17
19
  end
18
20
 
19
21
  def set_file_name(path_to_file)
@@ -31,10 +31,14 @@ module Watir
31
31
  end
32
32
 
33
33
  def locate
34
- return if [Element, TableBodies].include? self.class
35
- tag = self.class.const_defined?(:TAG) ? self.class::TAG : self.class.name.split("::").last
36
- @o = @container.tagged_element_locator(tag, @how, @what).locate
37
- end
34
+ return if self.class == Element
35
+
36
+ tags = @how.is_a?(Hash) && @how[:tag_name] ? [@how[:tag_name].upcase] :
37
+ self.class.const_defined?(:TAG) ? [self.class::TAG] :
38
+ self.class.const_defined?(:TAGS) ? self.class::TAGS :
39
+ [self.class.name.split("::").last.upcase]
40
+ @o = @container.locator_for(TaggedElementLocator, tags, @how, @what, self.class).locate
41
+ end
38
42
 
39
43
  # Return the ole object, allowing any methods of the DOM that Watir doesn't support to be used.
40
44
  def ole_object
@@ -50,19 +54,18 @@ module Watir
50
54
  end
51
55
 
52
56
  private
53
- def self.def_wrap(ruby_method_name, ole_method_name=nil)
54
- ole_method_name = ruby_method_name unless ole_method_name
55
- class_eval "def #{ruby_method_name}
57
+ def self.def_wrap(method_name, ole_method_name=nil)
58
+ class_eval "def #{method_name}
56
59
  assert_exists
57
- ole_object.invoke('#{ole_method_name}')
60
+ ole_object.invoke('#{ole_method_name || method_name}')
58
61
  end"
59
62
  end
60
63
 
61
- def self.def_wrap_guard(method_name)
64
+ def self.def_wrap_guard(method_name, ole_method_name = nil)
62
65
  class_eval "def #{method_name}
63
66
  assert_exists
64
67
  begin
65
- ole_object.invoke('#{method_name}')
68
+ ole_object.invoke('#{ole_method_name || method_name}')
66
69
  rescue
67
70
  ''
68
71
  end
@@ -74,8 +77,12 @@ module Watir
74
77
  def assert_exists
75
78
  locate
76
79
  unless ole_object
77
- raise UnknownObjectException.new(
78
- Watir::Exception.message_for_unable_to_locate(@how, @what))
80
+ if self.is_a?(Frame)
81
+ raise UnknownFrameException.new(Watir::Exception.message_for_unable_to_locate(@how, @what))
82
+ else
83
+ raise UnknownObjectException.new(
84
+ Watir::Exception.message_for_unable_to_locate(@how, @what))
85
+ end
79
86
  end
80
87
  end
81
88
 
@@ -97,10 +104,6 @@ module Watir
97
104
  # return the title of the element
98
105
  def_wrap_guard :title
99
106
 
100
- def_wrap_guard :currentstyle
101
- # return current style instead of the inline style of the element
102
- alias style currentstyle
103
-
104
107
  def_wrap_guard :alt
105
108
  def_wrap_guard :src
106
109
 
@@ -108,8 +111,6 @@ module Watir
108
111
  def_wrap_guard :type # input elements only
109
112
  # return the url the link points to
110
113
  def_wrap :href # link only
111
- # return the ID of the control that this label is associated with
112
- def_wrap :for, :htmlFor # label only
113
114
  # return the class name of the element
114
115
  # raise an ObjectNotFound exception if the object cannot be found
115
116
  def_wrap :class_name, :className
@@ -117,6 +118,55 @@ module Watir
117
118
  def_wrap :unique_number, :uniqueNumber
118
119
  # Return the outer html of the object - see http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/outerhtml.asp?frame=true
119
120
  def_wrap :html, :outerHTML
121
+ # Return the "for" attribute for label
122
+ def_wrap_guard :for, :htmlFor
123
+ # Return the content attribute for meta tag
124
+ def_wrap_guard :content
125
+ # Return the http-equiv attribute for meta tag
126
+ def_wrap_guard :http_equiv, :httpEquiv
127
+ # Return font tag attributes
128
+ def_wrap_guard :color
129
+ def_wrap_guard :face
130
+ def_wrap_guard :size
131
+ # Return option label attribute
132
+ def_wrap_guard :label
133
+ # Return table rules attribute
134
+ def_wrap_guard :rules
135
+ # Return td headers attribute
136
+ def_wrap_guard :headers
137
+
138
+ def tag_name
139
+ assert_exists
140
+ @o.tagName.downcase
141
+ end
142
+
143
+ # returns specific Element subclass for current Element
144
+ def to_subtype
145
+ assert_exists
146
+
147
+ tag = tag_name
148
+ if tag == "html"
149
+ html_element(:ole_object, ole_object)
150
+ elsif tag == "input"
151
+ self.send(ole_object.invoke('type'), :ole_object, ole_object)
152
+ elsif tag == "select"
153
+ self.select_list(:ole_object, ole_object)
154
+ else
155
+ self.send(tag.downcase, :ole_object, ole_object)
156
+ end
157
+ end
158
+
159
+ # send keys to element
160
+ def send_keys(key_string)
161
+ focus
162
+ page_container.send_keys key_string
163
+ end
164
+
165
+ # return the css style as a string
166
+ def style
167
+ assert_exists
168
+ ole_object.style.cssText
169
+ end
120
170
 
121
171
  # return the text before the element
122
172
  def before_text # label only
@@ -138,18 +188,18 @@ module Watir
138
188
  end
139
189
  end
140
190
 
141
- # Return the innerText of the object
191
+ # Return the innerText of the object or an empty string if the object is
192
+ # not visible
142
193
  # Raise an ObjectNotFound exception if the object cannot be found
143
194
  def text
144
195
  assert_exists
145
- return ole_object.innerText.strip
196
+ visible? ? ole_object.innerText.strip : ""
146
197
  end
147
198
 
148
199
  # IE9 only returns empty string for ole_object.name for non-input elements
149
200
  # so get at it through the attribute which will make the matchers work
150
201
  def name
151
- assert_exists
152
- ole_object.getAttribute('name') || ''
202
+ attribute_value('name') || ''
153
203
  end
154
204
 
155
205
  def __ole_inner_elements
@@ -165,7 +215,9 @@ module Watir
165
215
  # Return the element immediately containing self.
166
216
  def parent
167
217
  assert_exists
168
- result = Element.new(ole_object.parentelement)
218
+ parent_element = ole_object.parentelement
219
+ return unless parent_element
220
+ result = Element.new(parent_element).to_subtype
169
221
  result.set_container self
170
222
  result
171
223
  end
@@ -178,6 +230,8 @@ module Watir
178
230
  ole_object.sourceindex <=> other.ole_object.sourceindex
179
231
  end
180
232
 
233
+ alias_method :eql?, :==
234
+
181
235
  # Return true if self is contained earlier in the html than other.
182
236
  alias :before? :<
183
237
  # Return true if self is contained later in the html than other.
@@ -256,9 +310,18 @@ module Watir
256
310
  @container.wait
257
311
  end
258
312
 
313
+ def right_click
314
+ perform_action {fire_event("oncontextmenu"); @container.wait}
315
+ end
316
+
317
+ def double_click
318
+ perform_action {fire_event("ondblclick"); @container.wait}
319
+ end
320
+
259
321
  def replace_method(method)
260
322
  method == 'click' ? 'click!' : method
261
323
  end
324
+
262
325
  private :replace_method
263
326
 
264
327
  def build_method(method_name, *args)
@@ -271,6 +334,7 @@ module Watir
271
334
  end
272
335
  "#{replace_method(method_name)}(#{arguments.join(',')})"
273
336
  end
337
+
274
338
  private :build_method
275
339
 
276
340
  def generate_ruby_code(element, method_name, *args)
@@ -280,6 +344,7 @@ module Watir
280
344
  "require '#{File.expand_path(File.dirname(__FILE__))}/core';#{element}.#{method};"
281
345
  return ruby_code
282
346
  end
347
+
283
348
  private :generate_ruby_code
284
349
 
285
350
  def spawned_no_wait_command(command)
@@ -297,15 +362,12 @@ module Watir
297
362
  private :spawned_no_wait_command
298
363
 
299
364
  def click!
300
- assert_exists
301
- assert_enabled
302
-
303
- highlight(:set)
304
- # Not sure why but in IE9 Document mode, passing a parameter
305
- # to click seems to work. Firing the onClick event breaks other tests
306
- # so this seems to be the safest change and also works fine in IE8
307
- ole_object.click(0)
308
- highlight(:clear)
365
+ perform_action do
366
+ # Not sure why but in IE9 Document mode, passing a parameter
367
+ # to click seems to work. Firing the onClick event breaks other tests
368
+ # so this seems to be the safest change and also works fine in IE8
369
+ ole_object.click(0)
370
+ end
309
371
  end
310
372
 
311
373
  # Flash the element the specified number of times.
@@ -326,15 +388,12 @@ module Watir
326
388
  # raises: UnknownObjectException if the object is not found
327
389
  # ObjectDisabledException if the object is currently disabled
328
390
  def fire_event(event)
329
- assert_exists
330
- assert_enabled
331
- highlight(:set)
332
- dispatch_event(event)
333
- @container.wait
334
- highlight(:clear)
391
+ perform_action {dispatch_event(event); @container.wait}
335
392
  end
336
393
 
337
394
  def dispatch_event(event)
395
+ assert_exists
396
+
338
397
  if IE.version_parts.first.to_i >= 9
339
398
  if @container.page_container.document_mode.to_i >= 9
340
399
  ole_object.dispatchEvent(create_event(event))
@@ -347,34 +406,34 @@ module Watir
347
406
  end
348
407
 
349
408
  def create_event(event)
350
- event =~ /on(.*)/i
351
- event = $1 if $1
352
- event.downcase!
353
- # See http://www.howtocreate.co.uk/tutorials/javascript/domevents
354
- case event
355
- when 'abort', 'blur', 'change', 'error', 'focus', 'load',
356
- 'reset', 'resize', 'scroll', 'select', 'submit', 'unload'
357
- event_name = :initEvent
358
- event_type = 'HTMLEvents'
359
- event_args = [event, true, true]
360
- when 'keydown', 'keypress', 'keyup'
361
- event_name = :initKeyboardEvent
362
- event_type = 'KeyboardEvent'
363
- # 'type', bubbles, cancelable, windowObject, ctrlKey, altKey, shiftKey, metaKey, keyCode, charCode
364
- event_args = [event, true, true, @container.page_container.document.parentWindow.window, false, false, false, false, 0, 0]
365
- when 'click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup',
366
- 'contextmenu', 'drag', 'dragstart', 'dragenter', 'dragover', 'dragleave', 'dragend', 'drop', 'selectstart'
367
- event_name = :initMouseEvent
368
- event_type = 'MouseEvents'
369
- # 'type', bubbles, cancelable, windowObject, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget
370
- event_args = [event, true, true, @container.page_container.document.parentWindow.window, 1, 0, 0, 0, 0, false, false, false, false, 0, @container.page_container.document]
371
- else
372
- raise UnhandledEventException, "Don't know how to trigger event '#{event}'"
373
- end
374
- event = @container.page_container.document.createEvent(event_type)
375
- event.send event_name, *event_args
376
- event
409
+ event =~ /on(.*)/i
410
+ event = $1 if $1
411
+ event.downcase!
412
+ # See http://www.howtocreate.co.uk/tutorials/javascript/domevents
413
+ case event
414
+ when 'abort', 'blur', 'change', 'error', 'focus', 'load',
415
+ 'reset', 'resize', 'scroll', 'select', 'submit', 'unload'
416
+ event_name = :initEvent
417
+ event_type = 'HTMLEvents'
418
+ event_args = [event, true, true]
419
+ when 'keydown', 'keypress', 'keyup'
420
+ event_name = :initKeyboardEvent
421
+ event_type = 'KeyboardEvent'
422
+ # 'type', bubbles, cancelable, windowObject, ctrlKey, altKey, shiftKey, metaKey, keyCode, charCode
423
+ event_args = [event, true, true, @container.page_container.document.parentWindow.window, false, false, false, false, 0, 0]
424
+ when 'click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup',
425
+ 'contextmenu', 'drag', 'dragstart', 'dragenter', 'dragover', 'dragleave', 'dragend', 'drop', 'selectstart'
426
+ event_name = :initMouseEvent
427
+ event_type = 'MouseEvents'
428
+ # 'type', bubbles, cancelable, windowObject, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget
429
+ event_args = [event, true, true, @container.page_container.document.parentWindow.window, 1, 0, 0, 0, 0, false, false, false, false, 0, @container.page_container.document]
430
+ else
431
+ raise UnhandledEventException, "Don't know how to trigger event '#{event}'"
377
432
  end
433
+ event = @container.page_container.document.createEvent(event_type)
434
+ event.send event_name, *event_args
435
+ event
436
+ end
378
437
 
379
438
  # This method sets focus on the active element.
380
439
  # raises: UnknownObjectException if the object is not found
@@ -382,6 +441,7 @@ module Watir
382
441
  def focus
383
442
  assert_exists
384
443
  assert_enabled
444
+ @page_container.focus
385
445
  ole_object.focus
386
446
  end
387
447
 
@@ -401,7 +461,7 @@ module Watir
401
461
  # raises: UnknownObjectException if the object is not found
402
462
  def enabled?
403
463
  assert_exists
404
- return ! disabled
464
+ !disabled
405
465
  end
406
466
 
407
467
  # If any parent element isn't visible then we cannot write to the
@@ -432,36 +492,32 @@ module Watir
432
492
  # Returns null if attribute doesn't exist.
433
493
  def attribute_value(attribute_name)
434
494
  assert_exists
435
- return ole_object.getAttribute(attribute_name)
495
+ ole_object.getAttribute(attribute_name)
496
+ end
497
+
498
+ def perform_action
499
+ assert_exists
500
+ assert_enabled
501
+ highlight(:set)
502
+ yield
503
+ highlight(:clear)
436
504
  end
437
505
 
506
+ private :perform_action
507
+
438
508
  def method_missing(method_name, *args, &block)
439
- if method_name.to_s =~ /(.*)_no_wait/ && self.respond_to?($1)
440
- assert_exists
441
- assert_enabled
442
- highlight(:set)
443
- ruby_code = generate_ruby_code(self, $1, *args)
444
- system(spawned_no_wait_command(ruby_code))
445
- highlight(:clear)
509
+ meth = method_name.to_s
510
+ if meth =~ /(.*)_no_wait/ && self.respond_to?($1)
511
+ perform_action do
512
+ ruby_code = generate_ruby_code(self, $1, *args)
513
+ system(spawned_no_wait_command(ruby_code))
514
+ end
515
+ elsif meth =~ /^data_(.*)/
516
+ self.send(:attribute_value, meth.gsub("_", "-")) || ''
446
517
  else
447
518
  super
448
519
  end
449
520
  end
450
- end
451
-
452
- class ElementMapper # Still to be used
453
- include Container
454
-
455
- def initialize wrapper_class, container, how, what
456
- @wrapper_class = wrapper_class
457
- set_container
458
- @how = how
459
- @what = what
460
- end
461
-
462
- def method_missing method, *args
463
- locate
464
- @wrapper_class.new(@o).send(method, *args)
465
- end
521
+
466
522
  end
467
523
  end