watir-webdriver 0.6.4 → 0.6.5

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +78 -126
  3. data/LICENSE +3 -1
  4. data/README.md +1 -1
  5. data/Rakefile +1 -1
  6. data/lib/watir-webdriver.rb +15 -1
  7. data/lib/watir-webdriver/browser.rb +2 -1
  8. data/lib/watir-webdriver/cookies.rb +35 -7
  9. data/lib/watir-webdriver/elements/area.rb +13 -0
  10. data/lib/watir-webdriver/elements/element.rb +3 -3
  11. data/lib/watir-webdriver/elements/generated.rb +94 -55
  12. data/lib/watir-webdriver/elements/{frame.rb → iframe.rb} +41 -28
  13. data/lib/watir-webdriver/elements/input.rb +0 -16
  14. data/lib/watir-webdriver/elements/link.rb +13 -1
  15. data/lib/watir-webdriver/elements/text_area.rb +17 -0
  16. data/lib/watir-webdriver/html/generator.rb +2 -2
  17. data/lib/watir-webdriver/html/spec_extractor.rb +31 -4
  18. data/lib/watir-webdriver/html/visitor.rb +12 -4
  19. data/lib/watir-webdriver/locators/button_locator.rb +7 -13
  20. data/lib/watir-webdriver/locators/element_locator.rb +9 -5
  21. data/lib/watir-webdriver/locators/text_area_locator.rb +20 -0
  22. data/lib/watir-webdriver/locators/text_field_locator.rb +8 -0
  23. data/lib/watir-webdriver/version.rb +1 -1
  24. data/lib/watir-webdriver/wait.rb +23 -11
  25. data/spec/browser_spec.rb +18 -18
  26. data/spec/click_spec.rb +5 -5
  27. data/spec/container_spec.rb +5 -11
  28. data/spec/element_locator_spec.rb +32 -26
  29. data/spec/element_spec.rb +9 -9
  30. data/spec/implementation.rb +6 -2
  31. data/spec/input_spec.rb +11 -5
  32. data/spec/locator_spec_helper.rb +3 -3
  33. data/spec/special_chars_spec.rb +1 -1
  34. data/support/travis.sh +19 -5
  35. data/watir-webdriver.gemspec +1 -0
  36. metadata +37 -38
  37. data/spec/html/wait.html +0 -28
  38. data/spec/wait_spec.rb +0 -118
@@ -1,12 +1,13 @@
1
1
  # encoding: utf-8
2
2
  module Watir
3
- class Frame < HTMLElement
3
+ class IFrame < HTMLElement
4
4
 
5
5
  def locate
6
6
  @parent.assert_exists
7
7
 
8
- element = locate_iframe || locate_frame
9
- element or raise UnknownFrameException, "unable to locate frame/iframe using #{selector_string}"
8
+ locator = locator_class.new(@parent.wd, @selector.merge(:tag_name => tag_name), self.class.attribute_list)
9
+ element = locator.locate
10
+ element or raise UnknownFrameException, "unable to locate #{@selector[:tag_name]} using #{selector_string}"
10
11
 
11
12
  @parent.reset!
12
13
 
@@ -44,46 +45,58 @@ module Watir
44
45
 
45
46
  private
46
47
 
47
- def locate_iframe
48
- locator = locator_class.new(@parent.wd, @selector.merge(:tag_name => "iframe"), attribute_list)
49
- locator.locate
48
+ def tag_name
49
+ 'iframe'
50
50
  end
51
51
 
52
- def locate_frame
53
- locator = locator_class.new(@parent.wd, @selector.merge(:tag_name => "frame"), attribute_list)
54
- locator.locate
52
+ end # IFrame
53
+
54
+
55
+ class IFrameCollection < ElementCollection
56
+
57
+ def to_a
58
+ (0...elements.size).map { |idx| element_class.new @parent, :index => idx }
55
59
  end
56
60
 
57
- def attribute_list
58
- self.class.attribute_list | IFrame.attribute_list
61
+ def element_class
62
+ IFrame
59
63
  end
64
+
65
+ end # IFrameCollection
66
+
67
+
68
+ class Frame < IFrame
69
+
70
+ private
71
+
72
+ def tag_name
73
+ 'frame'
74
+ end
75
+
60
76
  end # Frame
61
77
 
78
+
79
+ class FrameCollection < IFrameCollection
80
+
81
+ def element_class
82
+ Frame
83
+ end
84
+
85
+ end # FrameCollection
86
+
87
+
62
88
  module Container
89
+
63
90
  def frame(*args)
64
- Frame.new(self, extract_selector(args))
91
+ Frame.new(self, extract_selector(args).merge(:tag_name => "frame"))
65
92
  end
66
93
 
67
94
  def frames(*args)
68
- FrameCollection.new(self, extract_selector(args).merge(:tag_name => /^(iframe|frame)$/)) # hack
95
+ FrameCollection.new(self, extract_selector(args).merge(:tag_name => "frame"))
69
96
  end
70
97
 
71
- def iframe(*args)
72
- warn "Watir::Container#iframe is replaced by Watir::Container#frame"
73
- frame(*args)
74
- end
75
-
76
- def iframes(*args)
77
- warn "Watir::Container#iframes is replaced by Watir::Container#frames"
78
- frame(*args)
79
- end
80
- end
98
+ end # Container
81
99
 
82
- class FrameCollection < ElementCollection
83
- def to_a
84
- (0...elements.size).map { |idx| element_class.new @parent, :index => idx }
85
- end
86
- end
87
100
 
88
101
  # @api private
89
102
  #
@@ -14,21 +14,5 @@ module Watir
14
14
  !disabled?
15
15
  end
16
16
 
17
- #
18
- # Return the type attribute of the element, or 'text' if the attribute is invalid.
19
- # TODO: discuss.
20
- #
21
- # @return [String]
22
- #
23
-
24
- def type
25
- assert_exists
26
- value = @element.attribute("type").to_s
27
-
28
- # we return 'text' if the type is invalid
29
- # not sure if we really should do this
30
- TextFieldLocator::NON_TEXT_TYPES.include?(value) ? value : 'text'
31
- end
32
-
33
17
  end # Input
34
18
  end # Watir
@@ -3,5 +3,17 @@ module Watir
3
3
  module Container
4
4
  alias_method :link, :a
5
5
  alias_method :links, :as
6
- end
6
+ end # Container
7
+
8
+
9
+ class Anchor < HTMLElement
10
+
11
+ #
12
+ # @todo temporarily add href attribute
13
+ #
14
+ # @see https://www.w3.org/Bugs/Public/show_bug.cgi?id=23192
15
+ #
16
+ attributes :string => [:href]
17
+
18
+ end # Anchor
7
19
  end # Watir
@@ -1,5 +1,22 @@
1
1
  module Watir
2
2
  class TextArea < HTMLElement
3
3
  include UserEditable
4
+
5
+ private
6
+
7
+ def locator_class
8
+ TextAreaLocator
9
+ end
10
+
4
11
  end # TextArea
12
+
13
+ class TextAreaCollection < ElementCollection
14
+
15
+ private
16
+
17
+ def locator_class
18
+ TextAreaLocator
19
+ end
20
+
21
+ end # TextAreaCollection
5
22
  end # Watir
@@ -45,6 +45,8 @@ module Watir
45
45
  # ignore the link element for now
46
46
  @tag2interfaces.delete("link")
47
47
  @sorted_interfaces.reject! { |intf| intf.name == "HTMLLinkElement" }
48
+ # frame is implemented manually, see https://github.com/watir/watir-webdriver/issues/204
49
+ @sorted_interfaces.reject! { |intf| intf.name == "HTMLFrameElement" }
48
50
  end
49
51
 
50
52
  def write_header
@@ -58,7 +60,6 @@ module Watir
58
60
  end
59
61
  end
60
62
 
61
-
62
63
  def write_container_methods
63
64
  @io.puts indent("module Container")
64
65
 
@@ -101,7 +102,6 @@ CODE
101
102
  @io.puts "end # Watir"
102
103
  end
103
104
 
104
-
105
105
  def indent(code, indent = 1)
106
106
  indent_string = " "*indent
107
107
  code.split("\n").map { |line| line.empty? ? line : indent_string + line }.join("\n")
@@ -3,6 +3,10 @@
3
3
  module Watir
4
4
  module HTML
5
5
  class SpecExtractor
6
+
7
+ class InterfaceNotFound < StandardError
8
+ end
9
+
6
10
  def initialize(uri)
7
11
  @uri = uri
8
12
  end
@@ -39,7 +43,7 @@ module Watir
39
43
  end
40
44
 
41
45
  def fetch_interface(interface)
42
- @interfaces_by_name[interface] or raise "#{interface} not found in IDL"
46
+ @interfaces_by_name[interface] or raise InterfaceNotFound, "#{interface} not found in IDL"
43
47
  end
44
48
 
45
49
  private
@@ -51,11 +55,20 @@ module Watir
51
55
  def extract_idl_parts
52
56
  parsed = @doc.search("//pre[@class='idl']").map { |e| parse_idl(e.inner_text) }.compact
53
57
 
54
- @interfaces = parsed.map { |elements|
55
- elements.select { |e| e.kind_of? WebIDL::Ast::Interface }
56
- }.flatten
58
+ implements = []
59
+ @interfaces = []
60
+
61
+ parsed.flatten.each do |element|
62
+ case element
63
+ when WebIDL::Ast::Interface
64
+ @interfaces << element
65
+ when WebIDL::Ast::ImplementsStatement
66
+ implements << element
67
+ end
68
+ end
57
69
 
58
70
  @interfaces_by_name = @interfaces.group_by { |i| i.name }
71
+ apply_implements(implements)
59
72
  end
60
73
 
61
74
  def extract_interface_map
@@ -109,6 +122,20 @@ module Watir
109
122
  end
110
123
  end
111
124
 
125
+ def apply_implements(implements)
126
+ implements.each do |is|
127
+ implementor_name = is.implementor.gsub(/^::/, '')
128
+ implementee_name = is.implementee.gsub(/^::/, '')
129
+
130
+ begin
131
+ intf = fetch_interface(implementor_name).first
132
+ intf.implements << fetch_interface(implementee_name).first
133
+ rescue InterfaceNotFound => ex
134
+ puts ex.message
135
+ end
136
+ end
137
+ end
138
+
112
139
  def idl_parser
113
140
  @idl_parser ||= WebIDL::Parser::IDLParser.new
114
141
  end
@@ -36,7 +36,7 @@ module Watir
36
36
 
37
37
  [ :scope,
38
38
  [:block,
39
- element_class(interface.name, interface.members.select { |e| e.kind_of?(WebIDL::Ast::Attribute) }, parent),
39
+ element_class(interface.name, extract_attributes(interface), parent),
40
40
  collection_class(interface.name)
41
41
  ]
42
42
  ]
@@ -89,6 +89,13 @@ module Watir
89
89
  ]
90
90
  end
91
91
 
92
+ def extract_attributes(interface)
93
+ members = interface.members
94
+ members += interface.implements.flat_map(&:members)
95
+
96
+ members.select { |e| e.kind_of?(WebIDL::Ast::Attribute) }
97
+ end
98
+
92
99
  def collection_class(name)
93
100
  return if @already_defined.include?(name)
94
101
  @already_defined << name
@@ -114,7 +121,7 @@ module Watir
114
121
  type = ruby_type_for(a.type)
115
122
  attrs[type] << ruby_attribute_for(type, a.name)
116
123
  end
117
-
124
+
118
125
  call :attributes, [literal_hash(attrs)]
119
126
  end
120
127
 
@@ -152,7 +159,7 @@ module Watir
152
159
  :function
153
160
  when 'Boolean'
154
161
  :bool
155
- when 'Document'
162
+ when 'Document', 'DocumentFragment'
156
163
  :document
157
164
  when 'DOMTokenList', 'DOMSettableTokenList'
158
165
  :token_list
@@ -173,7 +180,8 @@ module Watir
173
180
  when 'Element'
174
181
  :element
175
182
  when 'WindowProxy', 'ValidityState', 'MediaError', 'TimeRanges', 'Location',
176
- 'Any', 'TimedTrackArray', 'TimedTrack', 'TextTrackArray', 'TextTrack', 'MediaController'
183
+ 'Any', 'TimedTrackArray', 'TimedTrack', 'TextTrackArray', 'TextTrack',
184
+ 'MediaController', 'TextTrackKind'
177
185
  # probably completely wrong.
178
186
  :string
179
187
  else
@@ -41,25 +41,19 @@ module Watir
41
41
  def lhs_for(key)
42
42
  if @building == :input && key == :text
43
43
  "@value"
44
- elsif @building == :button && key == :value
45
- "text()"
46
44
  else
47
45
  super
48
46
  end
49
47
  end
50
48
 
51
- def matches_selector?(element, rx_selector)
52
- rx_selector = rx_selector.dup
53
- tag_name = element.tag_name.downcase
54
-
55
- [:value, :caption].each do |key|
56
- if rx_selector.has_key?(key)
57
- correct_key = tag_name == 'button' ? :text : :value
58
- rx_selector[correct_key] = rx_selector.delete(key)
59
- end
49
+ def equal_pair(key, value)
50
+ if @building == :button && key == :value
51
+ # :value should look for both node text and @value attribute
52
+ text = XpathSupport.escape(value)
53
+ "(text()=#{text} or @value=#{text})"
54
+ else
55
+ super
60
56
  end
61
-
62
- super
63
57
  end
64
58
 
65
59
  def tag_name_matches?(tag_name, _)
@@ -10,12 +10,14 @@ module Watir
10
10
  :id,
11
11
  :link,
12
12
  :link_text,
13
- # :name, # deliberately excluded to be watirspec compliant
13
+ :name,
14
14
  :partial_link_text,
15
15
  :tag_name,
16
16
  :xpath
17
17
  ]
18
18
 
19
+ WILDCARD_ATTRIBUTE = /^(aria|data)_(.+)$/
20
+
19
21
  def initialize(wd, selector, valid_attributes)
20
22
  @wd = wd
21
23
  @selector = selector.dup
@@ -32,8 +34,10 @@ module Watir
32
34
  element = find_first_by_multiple
33
35
  end
34
36
 
35
- # this actually only applies when finding by xpath - browser.text_field(:xpath, "//input[@type='radio']")
36
- # we don't need to validate the element if we built the xpath ourselves.
37
+ # This actually only applies when finding by xpath - browser.text_field(:xpath, "//input[@type='radio']")
38
+ # We don't need to validate the element if we built the xpath ourselves.
39
+ # It is also used to alter behavior of methods locating more than one type of element
40
+ # (e.g. text_field locates both input and textarea)
37
41
  validate_element(element) if element
38
42
  rescue Selenium::WebDriver::Error::NoSuchElementError
39
43
  nil
@@ -234,7 +238,7 @@ module Watir
234
238
  end
235
239
 
236
240
  def assert_valid_as_attribute(attribute)
237
- unless valid_attribute? attribute or attribute.to_s =~ /^data_.+$/
241
+ unless valid_attribute? attribute or attribute.to_s =~ WILDCARD_ATTRIBUTE
238
242
  raise MissingWayOfFindingObjectException, "invalid attribute: #{attribute.inspect}"
239
243
  end
240
244
  end
@@ -269,7 +273,7 @@ module Watir
269
273
  end
270
274
 
271
275
  def should_use_label_element?
272
- @selector[:tag_name] != "option"
276
+ !valid_attribute?(:label)
273
277
  end
274
278
 
275
279
  def build_wd_selector(selectors)
@@ -0,0 +1,20 @@
1
+ module Watir
2
+ class TextAreaLocator < ElementLocator
3
+
4
+ private
5
+
6
+ def normalize_selector(how, what)
7
+ # We need to iterate through located elements and fetch
8
+ # attribute value using WebDriver because XPath doesn't understand
9
+ # difference between IDL vs content attribute.
10
+ # Current ElementLocator design doesn't allow to do that in any
11
+ # obvious way except to use regular expression.
12
+ if how == :value && what.kind_of?(String)
13
+ [how, Regexp.new('^' + Regexp.escape(what) + '$')]
14
+ else
15
+ super
16
+ end
17
+ end
18
+
19
+ end # TextAreaLocator
20
+ end # Watir
@@ -68,5 +68,13 @@ module Watir
68
68
  el = super
69
69
  el if el and not NON_TEXT_TYPES.include? el.attribute(:type)
70
70
  end
71
+
72
+ def validate_element(element)
73
+ if element.tag_name.downcase == 'textarea'
74
+ warn "Locating textareas with '#text_field' is deprecated. Please, use '#textarea' method instead."
75
+ end
76
+ super
77
+ end
78
+
71
79
  end # TextFieldLocator
72
80
  end # Watir
@@ -1,3 +1,3 @@
1
1
  module Watir
2
- VERSION = "0.6.4"
2
+ VERSION = "0.6.5"
3
3
  end
@@ -1,4 +1,6 @@
1
1
  # encoding: utf-8
2
+ require 'forwardable'
3
+
2
4
  module Watir
3
5
  module Wait
4
6
 
@@ -7,6 +9,7 @@ module Watir
7
9
  INTERVAL = 0.1
8
10
 
9
11
  class << self
12
+
10
13
  #
11
14
  # Waits until the block evaluates to true or times out.
12
15
  #
@@ -18,10 +21,9 @@ module Watir
18
21
  # @raise [TimeoutError] if timeout is exceeded
19
22
  #
20
23
 
21
- def until(timeout = 30, message = nil, &block)
22
- end_time = ::Time.now + timeout
23
-
24
- until ::Time.now > end_time
24
+ def until(timeout = nil, message = nil, &block)
25
+ timeout ||= Watir.default_timeout
26
+ wait(timeout) do
25
27
  result = yield(self)
26
28
  return result if result
27
29
  sleep INTERVAL
@@ -41,10 +43,9 @@ module Watir
41
43
  # @raise [TimeoutError] if timeout is exceeded
42
44
  #
43
45
 
44
- def while(timeout = 30, message = nil, &block)
45
- end_time = ::Time.now + timeout
46
-
47
- until ::Time.now > end_time
46
+ def while(timeout = nil, message = nil, &block)
47
+ timeout ||= Watir.default_timeout
48
+ wait(timeout) do
48
49
  return unless yield(self)
49
50
  sleep INTERVAL
50
51
  end
@@ -61,6 +62,10 @@ module Watir
61
62
  err
62
63
  end
63
64
 
65
+ def wait(timeout, &block)
66
+ (timeout / INTERVAL).to_i.times &block
67
+ end
68
+
64
69
  end # self
65
70
  end # Wait
66
71
 
@@ -80,6 +85,10 @@ module Watir
80
85
  #
81
86
 
82
87
  class WhenPresentDecorator
88
+ extend Forwardable
89
+
90
+ def_delegator :@element, :present?
91
+
83
92
  def initialize(element, timeout, message = nil)
84
93
  @element = element
85
94
  @timeout = timeout
@@ -122,7 +131,8 @@ module Watir
122
131
  # @see Watir::Element#present?
123
132
  #
124
133
 
125
- def when_present(timeout = 30)
134
+ def when_present(timeout = nil)
135
+ timeout ||= Watir.default_timeout
126
136
  message = "waiting for #{selector_string} to become present"
127
137
 
128
138
  if block_given?
@@ -145,7 +155,8 @@ module Watir
145
155
  # @see Watir::Element#present?
146
156
  #
147
157
 
148
- def wait_until_present(timeout = 30)
158
+ def wait_until_present(timeout = nil)
159
+ timeout ||= Watir.default_timeout
149
160
  message = "waiting for #{selector_string} to become present"
150
161
  Watir::Wait.until(timeout, message) { present? }
151
162
  end
@@ -162,7 +173,8 @@ module Watir
162
173
  # @see Watir::Element#present?
163
174
  #
164
175
 
165
- def wait_while_present(timeout = 30)
176
+ def wait_while_present(timeout = nil)
177
+ timeout ||= Watir.default_timeout
166
178
  message = "waiting for #{selector_string} to disappear"
167
179
  Watir::Wait.while(timeout, message) { present? }
168
180
  rescue Selenium::WebDriver::Error::ObsoleteElementError