watir 6.9.1 → 6.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +8 -0
  3. data/lib/watir/capabilities.rb +2 -2
  4. data/lib/watir/elements/element.rb +5 -3
  5. data/lib/watir/elements/iframe.rb +2 -2
  6. data/lib/watir/exception.rb +0 -1
  7. data/lib/watir/locators/button/locator.rb +3 -10
  8. data/lib/watir/locators/button/validator.rb +1 -1
  9. data/lib/watir/locators/cell/locator.rb +3 -5
  10. data/lib/watir/locators/element/locator.rb +61 -105
  11. data/lib/watir/locators/element/selector_builder.rb +11 -4
  12. data/lib/watir/locators/row/locator.rb +3 -5
  13. data/lib/watir/locators/text_field/locator.rb +2 -13
  14. data/spec/browser_spec.rb +4 -3
  15. data/spec/element_locator_spec.rb +50 -19
  16. data/spec/locator_spec_helper.rb +1 -1
  17. data/spec/spec_helper.rb +1 -1
  18. data/spec/watirspec/elements/area_spec.rb +0 -4
  19. data/spec/watirspec/elements/button_spec.rb +0 -4
  20. data/spec/watirspec/elements/checkbox_spec.rb +0 -4
  21. data/spec/watirspec/elements/dd_spec.rb +0 -4
  22. data/spec/watirspec/elements/del_spec.rb +0 -4
  23. data/spec/watirspec/elements/div_spec.rb +30 -8
  24. data/spec/watirspec/elements/divs_spec.rb +1 -1
  25. data/spec/watirspec/elements/dl_spec.rb +0 -4
  26. data/spec/watirspec/elements/dt_spec.rb +0 -4
  27. data/spec/watirspec/elements/element_spec.rb +25 -12
  28. data/spec/watirspec/elements/elements_spec.rb +11 -0
  29. data/spec/watirspec/elements/em_spec.rb +0 -4
  30. data/spec/watirspec/elements/filefield_spec.rb +0 -4
  31. data/spec/watirspec/elements/form_spec.rb +0 -4
  32. data/spec/watirspec/elements/frame_spec.rb +0 -4
  33. data/spec/watirspec/elements/hidden_spec.rb +0 -4
  34. data/spec/watirspec/elements/hn_spec.rb +0 -4
  35. data/spec/watirspec/elements/iframe_spec.rb +0 -4
  36. data/spec/watirspec/elements/image_spec.rb +0 -4
  37. data/spec/watirspec/elements/ins_spec.rb +0 -4
  38. data/spec/watirspec/elements/label_spec.rb +0 -4
  39. data/spec/watirspec/elements/li_spec.rb +0 -4
  40. data/spec/watirspec/elements/link_spec.rb +2 -5
  41. data/spec/watirspec/elements/links_spec.rb +1 -1
  42. data/spec/watirspec/elements/map_spec.rb +0 -4
  43. data/spec/watirspec/elements/ol_spec.rb +4 -6
  44. data/spec/watirspec/elements/option_spec.rb +0 -13
  45. data/spec/watirspec/elements/p_spec.rb +0 -4
  46. data/spec/watirspec/elements/pre_spec.rb +0 -4
  47. data/spec/watirspec/elements/radio_spec.rb +0 -4
  48. data/spec/watirspec/elements/select_list_spec.rb +4 -6
  49. data/spec/watirspec/elements/span_spec.rb +2 -5
  50. data/spec/watirspec/elements/spans_spec.rb +1 -1
  51. data/spec/watirspec/elements/strong_spec.rb +0 -4
  52. data/spec/watirspec/elements/table_spec.rb +6 -6
  53. data/spec/watirspec/elements/tbody_spec.rb +0 -5
  54. data/spec/watirspec/elements/td_spec.rb +2 -5
  55. data/spec/watirspec/elements/text_field_spec.rb +0 -4
  56. data/spec/watirspec/elements/tfoot_spec.rb +0 -5
  57. data/spec/watirspec/elements/thead_spec.rb +0 -5
  58. data/spec/watirspec/elements/tr_spec.rb +0 -4
  59. data/spec/watirspec/elements/ul_spec.rb +2 -5
  60. data/spec/watirspec/html/multiple_ids.html +1 -0
  61. data/spec/watirspec/html/non_control_elements.html +6 -1
  62. data/spec/watirspec/radio_set_spec.rb +0 -4
  63. data/watir.gemspec +1 -1
  64. metadata +2 -4
  65. data/spec/watirspec/html/ng_attributes.html +0 -59
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: df2330f15858aff8a2b86569ddc5d912bf98b0a0
4
- data.tar.gz: '087b9e1452a7aba1db5ca249f37d0a54652a8b72'
3
+ metadata.gz: ec3596aac49e15a562c7085702a1e246fa0abb5e
4
+ data.tar.gz: c2d652b8c6de8895b0004581ca07fc69e42d4d8f
5
5
  SHA512:
6
- metadata.gz: c9e0205fed4dda96b3fbff910a607cff10df4043c5c3c16c6a28a51d4ff46d6567a7ff14f1aa2707b4568e81e23a6d58ed7ad831174081964e3d01c398e1a5dc
7
- data.tar.gz: b481f2d349f880e1c23d7d74a5309848200da4e311e158ad1b592f7ac680c894183336b7499b88543702f458a62eea5cc456404e4d812ecc7fd33d5a0f151102
6
+ metadata.gz: f8a5cca75e7d14deaaa47f6eba851a2ff082b6522bfef4f7d2b485f97040e2086142d58641ee51939e320c272bbc5a7fb3a6325b99db1c9cbbfc368b2ef95eb2
7
+ data.tar.gz: fe12a324b627005ddd14f8473c00f7b8fd6fa3ccac864196af5220cdbd07aaf9150bbe52d87e148b5741dae2b50735fcf6c28b7ac69324b55fd662975f3382a4
data/CHANGES.md CHANGED
@@ -1,3 +1,11 @@
1
+ ### 6.10.0 (2017-11-23)
2
+
3
+ * Add support for locating elements with custom attributes
4
+ * Add new `:visible_text` locator (thanks Justin Ko)
5
+ * Deprecate Selenium locators `:link`, `:link_text` and `:partial_link_text` in favor of `:visible_text`
6
+ * Deprecate finding only visible text for `RegExp` values on `:text` locator (#342)
7
+ * Improve support for finding elements with `:tag_name` along with `:xpath` or `:css` locators
8
+
1
9
  ### 6.9.1 (2017-11-20)
2
10
 
3
11
  * Fix bug preventing the use of `#exectue_script` in `AfterHook` (#684)
@@ -50,7 +50,7 @@ module Watir
50
50
 
51
51
  %i(open_timeout read_timeout client_timeout).each do |t|
52
52
  next if http_client.nil? || !respond_to?(t)
53
- warn "You can now pass #{t} value directly into Watir::Browser opt without needing to use :http_client"
53
+ Watir.logger.warn "You can now pass #{t} value directly into Watir::Browser opt without needing to use :http_client"
54
54
  end
55
55
 
56
56
  http_client ||= Selenium::WebDriver::Remote::Http::Default.new
@@ -111,7 +111,7 @@ module Watir
111
111
  caps = @options.delete(:desired_capabilities)
112
112
 
113
113
  if caps
114
- warn 'You can now pass values directly into Watir::Browser opt without needing to use :desired_capabilities'
114
+ Watir.logger.warn 'You can now pass values directly into Watir::Browser opt without needing to use :desired_capabilities'
115
115
  @selenium_opts.merge!(@options)
116
116
  else
117
117
  caps = Selenium::WebDriver::Remote::Capabilities.send @browser, @options
@@ -587,9 +587,9 @@ module Watir
587
587
 
588
588
  element_validator = element_validator_class.new
589
589
  selector_builder = selector_builder_class.new(@query_scope, @selector.dup, self.class.attribute_list)
590
- locator = locator_class.new(@query_scope, @selector.dup, selector_builder, element_validator)
590
+ @locator = locator_class.new(@query_scope, @selector.dup, selector_builder, element_validator)
591
591
 
592
- @element = locator.locate
592
+ @element = @locator.locate
593
593
  end
594
594
 
595
595
  def selector_string
@@ -657,7 +657,9 @@ module Watir
657
657
  yield
658
658
  rescue unknown_exception => ex
659
659
  msg = ex.message
660
- msg += ". Maybe look in an iframe?" if @query_scope.iframes.count > 0
660
+ msg += "; Maybe look in an iframe?" if @query_scope.iframes.count > 0
661
+ custom_attributes = @locator.selector_builder.custom_attributes
662
+ msg += "; Watir treated #{custom_attributes} as a non-HTML compliant attribute, ensure that was intended" unless custom_attributes.empty?
661
663
  raise unknown_exception, msg
662
664
  rescue Selenium::WebDriver::Error::StaleElementReferenceError
663
665
  retry
@@ -8,9 +8,9 @@ module Watir
8
8
  selector = @selector.merge(tag_name: frame_tag)
9
9
  element_validator = element_validator_class.new
10
10
  selector_builder = selector_builder_class.new(@query_scope, selector, self.class.attribute_list)
11
- locator = locator_class.new(@query_scope, selector, selector_builder, element_validator)
11
+ @locator = locator_class.new(@query_scope, selector, selector_builder, element_validator)
12
12
 
13
- element = locator.locate
13
+ element = @locator.locate
14
14
  element or raise unknown_exception, "unable to locate #{@selector[:tag_name]} using #{selector_string}"
15
15
 
16
16
  @element = FramedDriver.new(element, browser)
@@ -7,7 +7,6 @@ module Watir
7
7
  class ObjectDisabledException < Error; end
8
8
  class ObjectReadOnlyException < Error; end
9
9
  class NoValueFoundException < Error; end
10
- class MissingWayOfFindingObjectException < Error; end
11
10
  class UnknownCellException < Error; end
12
11
  class NoMatchingWindowFoundException < Error; end
13
12
  class UnknownFrameException < Error; end
@@ -2,19 +2,12 @@ module Watir
2
2
  module Locators
3
3
  class Button
4
4
  class Locator < Element::Locator
5
- def locate_all
6
- find_all_by_multiple
7
- end
8
5
 
9
- private
10
6
 
11
- def wd_find_first_by(how, what)
12
- if how == :tag_name
13
- how = :xpath
14
- what = ".//button | .//input[#{selector_builder.xpath_builder.attribute_expression(:input, type: Watir::Button::VALID_TYPES)}]"
15
- end
7
+ private
16
8
 
17
- super
9
+ def using_selenium(*)
10
+ # force watir usage
18
11
  end
19
12
 
20
13
  def can_convert_regexp_to_contains?
@@ -2,7 +2,7 @@ module Watir
2
2
  module Locators
3
3
  class Button
4
4
  class Validator < Element::Validator
5
- def validate(element, selector)
5
+ def validate(element, _selector)
6
6
  return unless %w[input button].include?(element.tag_name.downcase)
7
7
  # TODO - Verify this is desired behavior based on https://bugzilla.mozilla.org/show_bug.cgi?id=1290963
8
8
  return if element.tag_name.downcase == "input" && !Watir::Button::VALID_TYPES.include?(element.attribute(:type).downcase)
@@ -2,15 +2,13 @@ module Watir
2
2
  module Locators
3
3
  class Cell
4
4
  class Locator < Element::Locator
5
- def locate_all
6
- find_all_by_multiple
7
- end
8
5
 
9
6
  private
10
7
 
11
- def by_id
12
- nil
8
+ def using_selenium(*)
9
+ # force watir usage
13
10
  end
11
+
14
12
  end
15
13
  end
16
14
  end
@@ -36,124 +36,82 @@ module Watir
36
36
  end
37
37
 
38
38
  def locate
39
- e = by_id and return e # short-circuit if :id is given
40
-
41
- element = if @selector.size == 1
42
- find_first_by_one
43
- else
44
- find_first_by_multiple
45
- end
46
-
47
- # Validation not necessary if Watir builds the xpath
48
- return element unless @selector.key?(:xpath) || @selector.key?(:css)
49
- element_validator.validate(element, @selector) if element
39
+ using_selenium(:first) || using_watir(:first)
50
40
  rescue Selenium::WebDriver::Error::NoSuchElementError, Selenium::WebDriver::Error::StaleElementReferenceError
51
41
  nil
52
42
  end
53
43
 
54
44
  def locate_all
55
- if @selector.size == 1
56
- find_all_by_one
57
- else
58
- find_all_by_multiple
59
- end
45
+ return [@selector[:element]] if @selector.key?(:element)
46
+ using_selenium(:all) || using_watir(:all)
60
47
  end
61
48
 
62
49
  private
63
50
 
64
- def by_id
51
+ def using_selenium(filter = :first)
65
52
  selector = @selector.dup
66
- id = selector.delete(:id)
67
- return if !id.is_a?(String) || selector[:adjacent]
68
-
69
- tag_name = selector.delete(:tag_name)
70
- return unless selector.empty? # multiple attributes
71
-
72
- element = locate_element(:id, id)
73
- return if tag_name && !element_validator.validate(element, {tag_name: tag_name})
74
-
75
- element
76
- end
77
-
78
- def find_first_by_one
79
- how, what = @selector.to_a.first
80
- selector_builder.check_type(how, what)
81
-
82
- if wd_supported?(how, what)
83
- wd_find_first_by(how, what)
84
- else
85
- find_first_by_multiple
53
+ tag_name = selector[:tag_name].is_a?(::Symbol) ? selector[:tag_name].to_s : selector[:tag_name]
54
+ selector.delete(:tag_name) if selector.size > 1
55
+
56
+ WD_FINDERS.each do |sel|
57
+ next unless (value = selector.delete(sel))
58
+ return unless selector.empty? && wd_supported?(sel, value)
59
+ if filter == :all
60
+ found = locate_elements(sel, value)
61
+ return filter_elements_by_locator(found, tag_name: tag_name, filter: filter).compact
62
+ else
63
+ found = locate_element(sel, value)
64
+ return sel != :tag_name && tag_name && !validate([found], tag_name) ? nil : found
65
+ end
86
66
  end
67
+ nil
87
68
  end
88
69
 
89
- def find_first_by_multiple
70
+ def using_watir(filter = :first)
90
71
  selector = selector_builder.normalized_selector
72
+ visible = selector.delete(:visible)
73
+ visible_text = selector.delete(:visible_text)
74
+ tag_name = selector[:tag_name].is_a?(::Symbol) ? selector[:tag_name].to_s : selector[:tag_name]
75
+ validation_required = (selector.key?(:css) || selector.key?(:xpath)) && tag_name
91
76
 
77
+ if selector.key?(:index) && filter == :all
78
+ raise ArgumentError, "can't locate all elements by :index"
79
+ end
92
80
  idx = selector.delete(:index) unless selector[:adjacent]
93
- visible = selector.delete(:visible)
94
81
 
95
82
  how, what = selector_builder.build(selector)
96
83
 
97
- if how
98
- # could build xpath/css for selector
99
- if idx && idx != 0 || !visible.nil?
100
- elements = locate_elements(how, what)
101
- filter_elements elements, visible, idx, :single
102
- else
103
- locate_element(how, what)
104
- end
105
- else
106
- # can't use xpath, probably a regexp in there
107
- if idx && idx != 0 || !visible.nil?
108
- elements = wd_find_by_regexp_selector(selector, :select)
109
- filter_elements elements, visible, idx, :single
110
- else
111
- wd_find_by_regexp_selector(selector, :find)
112
- end
113
- end
114
- end
84
+ needs_filtering = idx && idx != 0 || !visible.nil? || !visible_text.nil? || validation_required || filter == :all
115
85
 
116
- def find_all_by_one
117
- how, what = @selector.to_a.first
118
- return [what] if how == :element
119
- selector_builder.check_type how, what
120
-
121
- if wd_supported?(how, what)
122
- wd_find_all_by(how, what)
86
+ if needs_filtering
87
+ matching = matching_elements(how, what, selector)
88
+ return filter_elements_by_locator(matching, visible, visible_text, idx, tag_name: tag_name, filter: filter)
89
+ elsif how
90
+ locate_element(how, what)
123
91
  else
124
- find_all_by_multiple
92
+ wd_find_by_regexp_selector(selector, :first)
125
93
  end
126
94
  end
127
95
 
128
- def find_all_by_multiple
129
- selector = selector_builder.normalized_selector
130
- visible = selector.delete(:visible)
131
-
132
- if selector.key? :index
133
- raise ArgumentError, "can't locate all elements by :index"
134
- end
135
-
136
- how, what = selector_builder.build(selector)
137
- found = if how
138
- locate_elements(how, what)
139
- else
140
- wd_find_by_regexp_selector(selector, :select)
141
- end
142
- return [] if found.nil?
143
- filter_elements found, visible, nil, :multiple
96
+ def validate(elements, tag_name)
97
+ elements.compact.all? { |element| element_validator.validate(element, {tag_name: tag_name}) }
144
98
  end
145
99
 
146
- def wd_find_all_by(how, what)
147
- if what.is_a? String
148
- locate_elements(how, what)
149
- else
150
- all_elements.select { |element| fetch_value(element, how) =~ what }
151
- end
100
+ def matching_elements(how, what, selector = nil)
101
+ found = how ? locate_elements(how, what) : wd_find_by_regexp_selector(selector, :all)
102
+ found || []
152
103
  end
153
104
 
154
105
  def fetch_value(element, how)
155
106
  case how
156
107
  when :text
108
+ vis = element.text
109
+ all = Watir::Element.new(@query_scope, element: element).send(:execute_js, :getTextContent, element).strip
110
+ unless all == vis.strip
111
+ Watir.logger.deprecate(':text locator with RegExp values to find elements based on only visible text', ":visible_text")
112
+ end
113
+ vis
114
+ when :visible_text
157
115
  element.text
158
116
  when :tag_name
159
117
  element.tag_name.downcase
@@ -168,15 +126,7 @@ module Watir
168
126
  locate_elements(:xpath, ".//*")
169
127
  end
170
128
 
171
- def wd_find_first_by(how, what)
172
- if what.is_a? String
173
- locate_element(how, what)
174
- else
175
- all_elements.find { |element| fetch_value(element, how) =~ what }
176
- end
177
- end
178
-
179
- def wd_find_by_regexp_selector(selector, method = :find)
129
+ def wd_find_by_regexp_selector(selector, filter)
180
130
  query_scope = ensure_scope_context
181
131
  rx_selector = delete_regexps_from(selector)
182
132
 
@@ -205,15 +155,18 @@ module Watir
205
155
  end
206
156
 
207
157
  elements = locate_elements(how, what, query_scope)
208
- filter_elements_by_regex(elements, rx_selector, method)
158
+ filter_elements_by_regex(elements, rx_selector, filter)
209
159
  end
210
160
 
211
- def filter_elements elements, visible, idx, number
161
+ def filter_elements_by_locator(elements, visible = nil, visible_text = nil, idx = nil, tag_name: nil, filter: :first)
212
162
  elements.select! { |el| visible == el.displayed? } unless visible.nil?
213
- number == :single ? elements[idx || 0] : elements
163
+ elements.select! { |el| visible_text === el.text } unless visible_text.nil?
164
+ elements.select! { |el| element_validator.validate(el, {tag_name: tag_name}) } unless tag_name.nil?
165
+ filter == :first ? elements[idx || 0] : elements
214
166
  end
215
167
 
216
- def filter_elements_by_regex(elements, selector, method)
168
+ def filter_elements_by_regex(elements, selector, filter)
169
+ method = filter == :first ? :find : :select
217
170
  elements.__send__(method) { |el| matches_selector?(el, selector) }
218
171
  end
219
172
 
@@ -262,8 +215,8 @@ module Watir
262
215
  @query_scope.wd
263
216
  end
264
217
 
265
- def locate_element(how, what)
266
- @query_scope.wd.find_element(how, what)
218
+ def locate_element(how, what, scope = @query_scope.wd)
219
+ scope.find_element(how, what)
267
220
  end
268
221
 
269
222
  def locate_elements(how, what, scope = @query_scope.wd)
@@ -271,9 +224,12 @@ module Watir
271
224
  end
272
225
 
273
226
  def wd_supported?(how, what)
274
- return false unless WD_FINDERS.include?(how)
275
- return false unless what.kind_of?(String) || what.kind_of?(Regexp)
276
- return false if [:class, :class_name].include?(how) && what.kind_of?(String) && what.include?(' ')
227
+ return false unless what.kind_of?(String)
228
+ return false if [:class, :class_name].include?(how) && what.include?(' ')
229
+ %i[partial_link_text link_text link].each do |loc|
230
+ next unless how == loc
231
+ Watir.logger.deprecate(":#{loc} locator", ':visible_text')
232
+ end
277
233
  true
278
234
  end
279
235
  end
@@ -2,6 +2,8 @@ module Watir
2
2
  module Locators
3
3
  class Element
4
4
  class SelectorBuilder
5
+ attr_reader :custom_attributes
6
+
5
7
  VALID_WHATS = [Array, String, Regexp, TrueClass, FalseClass, ::Symbol].freeze
6
8
  WILDCARD_ATTRIBUTE = /^(aria|data)_(.+)$/
7
9
 
@@ -9,6 +11,7 @@ module Watir
9
11
  @query_scope = query_scope # either element or browser
10
12
  @selector = selector
11
13
  @valid_attributes = valid_attributes
14
+ @custom_attributes = []
12
15
  end
13
16
 
14
17
  def normalized_selector
@@ -34,6 +37,10 @@ module Watir
34
37
  unless what.is_a?(TrueClass) || what.is_a?(FalseClass)
35
38
  raise TypeError, "expected TrueClass or FalseClass, got #{what.inspect}:#{what.class}"
36
39
  end
40
+ when :visible_text
41
+ unless what.is_a?(String) || what.is_a?(Regexp)
42
+ raise TypeError, "expected String or Regexp, got #{what.inspect}:#{what.class}"
43
+ end
37
44
  else
38
45
  if what.is_a?(Array) && how != :class && how != :class_name
39
46
  raise TypeError, "Only :class locator can have a value of an Array"
@@ -66,7 +73,7 @@ module Watir
66
73
 
67
74
  def normalize_selector(how, what)
68
75
  case how
69
- when :tag_name, :text, :xpath, :index, :class, :label, :css, :visible, :adjacent
76
+ when :tag_name, :text, :xpath, :index, :class, :label, :css, :visible, :visible_text, :adjacent
70
77
  # include :class since the valid attribute is 'class_name'
71
78
  # include :for since the valid attribute is 'html_for'
72
79
  [how, what]
@@ -75,14 +82,14 @@ module Watir
75
82
  when :caption
76
83
  [:text, what]
77
84
  else
78
- assert_valid_as_attribute how
85
+ check_custom_attribute how
79
86
  [how, what]
80
87
  end
81
88
  end
82
89
 
83
- def assert_valid_as_attribute(attribute)
90
+ def check_custom_attribute(attribute)
84
91
  return if valid_attribute?(attribute) || attribute.to_s =~ WILDCARD_ATTRIBUTE
85
- raise Exception::MissingWayOfFindingObjectException, "invalid attribute: #{attribute.inspect}"
92
+ @custom_attributes << attribute.to_s
86
93
  end
87
94
 
88
95
  def given_xpath_or_css(selector)
@@ -2,15 +2,13 @@ module Watir
2
2
  module Locators
3
3
  class Row
4
4
  class Locator < Element::Locator
5
- def locate_all
6
- find_all_by_multiple
7
- end
8
5
 
9
6
  private
10
7
 
11
- def by_id
12
- nil # avoid this
8
+ def using_selenium(*)
9
+ # force Watir usage
13
10
  end
11
+
14
12
  end
15
13
  end
16
14
  end