watir-webdriver 0.6.4 → 0.6.5

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