watir 2.0.4 → 3.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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