watir 6.13.0 → 6.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (223) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +141 -0
  3. data/.travis.yml +6 -0
  4. data/CHANGES.md +12 -0
  5. data/Gemfile +4 -10
  6. data/README.md +64 -49
  7. data/Rakefile +28 -16
  8. data/lib/watir.rb +13 -15
  9. data/lib/watir/adjacent.rb +15 -13
  10. data/lib/watir/after_hooks.rb +8 -10
  11. data/lib/watir/alert.rb +7 -8
  12. data/lib/watir/aliases.rb +2 -2
  13. data/lib/watir/attribute_helper.rb +18 -20
  14. data/lib/watir/browser.rb +42 -75
  15. data/lib/watir/capabilities.rb +19 -10
  16. data/lib/watir/cell_container.rb +0 -2
  17. data/lib/watir/container.rb +4 -4
  18. data/lib/watir/cookies.rb +7 -8
  19. data/lib/watir/element_collection.rb +37 -22
  20. data/lib/watir/elements/area.rb +0 -2
  21. data/lib/watir/elements/button.rb +1 -3
  22. data/lib/watir/elements/cell.rb +0 -1
  23. data/lib/watir/elements/checkbox.rb +5 -7
  24. data/lib/watir/elements/date_field.rb +5 -9
  25. data/lib/watir/elements/date_time_field.rb +6 -10
  26. data/lib/watir/elements/dlist.rb +2 -4
  27. data/lib/watir/elements/element.rb +201 -99
  28. data/lib/watir/elements/file_field.rb +3 -4
  29. data/lib/watir/elements/font.rb +2 -4
  30. data/lib/watir/elements/form.rb +0 -2
  31. data/lib/watir/elements/hidden.rb +3 -4
  32. data/lib/watir/elements/html_elements.rb +24 -76
  33. data/lib/watir/elements/iframe.rb +57 -71
  34. data/lib/watir/elements/image.rb +3 -4
  35. data/lib/watir/elements/input.rb +0 -2
  36. data/lib/watir/elements/link.rb +2 -5
  37. data/lib/watir/elements/list.rb +4 -4
  38. data/lib/watir/elements/option.rb +3 -6
  39. data/lib/watir/elements/radio.rb +4 -6
  40. data/lib/watir/elements/row.rb +0 -1
  41. data/lib/watir/elements/select.rb +41 -43
  42. data/lib/watir/elements/svg_elements.rb +0 -116
  43. data/lib/watir/elements/table.rb +1 -2
  44. data/lib/watir/elements/table_cell.rb +2 -3
  45. data/lib/watir/elements/text_field.rb +4 -6
  46. data/lib/watir/exception.rb +0 -1
  47. data/lib/watir/extensions/nokogiri.rb +2 -4
  48. data/lib/watir/generator.rb +3 -3
  49. data/lib/watir/generator/base.rb +10 -10
  50. data/lib/watir/generator/base/generator.rb +26 -29
  51. data/lib/watir/generator/base/idl_sorter.rb +34 -32
  52. data/lib/watir/generator/base/spec_extractor.rb +132 -114
  53. data/lib/watir/generator/base/util.rb +1 -3
  54. data/lib/watir/generator/base/visitor.rb +140 -140
  55. data/lib/watir/generator/html.rb +4 -4
  56. data/lib/watir/generator/html/generator.rb +2 -4
  57. data/lib/watir/generator/html/spec_extractor.rb +33 -33
  58. data/lib/watir/generator/html/visitor.rb +14 -14
  59. data/lib/watir/generator/svg.rb +3 -3
  60. data/lib/watir/generator/svg/generator.rb +1 -3
  61. data/lib/watir/generator/svg/spec_extractor.rb +35 -35
  62. data/lib/watir/generator/svg/visitor.rb +14 -14
  63. data/lib/watir/has_window.rb +2 -4
  64. data/lib/watir/js_execution.rb +7 -9
  65. data/lib/watir/js_snippets.rb +3 -3
  66. data/lib/watir/js_snippets/attributeValues.js +11 -0
  67. data/lib/watir/legacy_wait.rb +7 -12
  68. data/lib/watir/locators.rb +9 -11
  69. data/lib/watir/locators/button/locator.rb +2 -3
  70. data/lib/watir/locators/button/selector_builder.rb +9 -9
  71. data/lib/watir/locators/button/selector_builder/xpath.rb +1 -1
  72. data/lib/watir/locators/button/validator.rb +2 -2
  73. data/lib/watir/locators/cell/locator.rb +0 -2
  74. data/lib/watir/locators/cell/selector_builder.rb +3 -5
  75. data/lib/watir/locators/element/locator.rb +85 -64
  76. data/lib/watir/locators/element/selector_builder.rb +40 -38
  77. data/lib/watir/locators/element/selector_builder/xpath.rb +20 -18
  78. data/lib/watir/locators/element/validator.rb +1 -1
  79. data/lib/watir/locators/row/locator.rb +0 -2
  80. data/lib/watir/locators/row/selector_builder.rb +6 -9
  81. data/lib/watir/locators/text_area/selector_builder.rb +1 -1
  82. data/lib/watir/locators/text_field/locator.rb +1 -3
  83. data/lib/watir/locators/text_field/selector_builder.rb +5 -5
  84. data/lib/watir/locators/text_field/selector_builder/xpath.rb +1 -1
  85. data/lib/watir/locators/text_field/validator.rb +3 -2
  86. data/lib/watir/logger.rb +11 -21
  87. data/lib/watir/navigation.rb +49 -0
  88. data/lib/watir/radio_set.rb +17 -18
  89. data/lib/watir/row_container.rb +3 -5
  90. data/lib/watir/screenshot.rb +2 -4
  91. data/lib/watir/user_editable.rb +13 -8
  92. data/lib/watir/version.rb +3 -0
  93. data/lib/watir/wait.rb +56 -55
  94. data/lib/watir/wait/timer.rb +1 -3
  95. data/lib/watir/window.rb +36 -45
  96. data/lib/watir/xpath_support.rb +1 -3
  97. data/lib/watirspec.rb +11 -11
  98. data/lib/watirspec/guards.rb +10 -7
  99. data/lib/watirspec/implementation.rb +3 -4
  100. data/lib/watirspec/rake_tasks.rb +30 -29
  101. data/lib/watirspec/remote_server.rb +3 -3
  102. data/lib/watirspec/runner.rb +1 -2
  103. data/lib/watirspec/server.rb +3 -0
  104. data/lib/watirspec/server/app.rb +14 -6
  105. data/spec/implementation_spec.rb +9 -9
  106. data/spec/locator_spec_helper.rb +3 -4
  107. data/spec/spec_helper.rb +3 -7
  108. data/spec/unit/container_spec.rb +9 -10
  109. data/spec/unit/element_locator_spec.rb +224 -219
  110. data/spec/unit/logger_spec.rb +4 -4
  111. data/spec/unit/unit_helper.rb +0 -2
  112. data/spec/unit/wait_spec.rb +26 -28
  113. data/spec/watirspec/adjacent_spec.rb +130 -130
  114. data/spec/watirspec/after_hooks_spec.rb +63 -63
  115. data/spec/watirspec/alert_spec.rb +6 -6
  116. data/spec/watirspec/attributes_spec.rb +6 -6
  117. data/spec/watirspec/browser_spec.rb +161 -162
  118. data/spec/watirspec/click_spec.rb +9 -9
  119. data/spec/watirspec/cookies_spec.rb +15 -14
  120. data/spec/watirspec/drag_and_drop_spec.rb +15 -16
  121. data/spec/watirspec/element_hidden_spec.rb +19 -21
  122. data/spec/watirspec/elements/area_spec.rb +18 -21
  123. data/spec/watirspec/elements/areas_spec.rb +13 -15
  124. data/spec/watirspec/elements/button_spec.rb +96 -99
  125. data/spec/watirspec/elements/buttons_spec.rb +17 -19
  126. data/spec/watirspec/elements/checkbox_spec.rb +102 -100
  127. data/spec/watirspec/elements/checkboxes_spec.rb +13 -15
  128. data/spec/watirspec/elements/collections_spec.rb +35 -37
  129. data/spec/watirspec/elements/date_field_spec.rb +46 -47
  130. data/spec/watirspec/elements/date_fields_spec.rb +13 -15
  131. data/spec/watirspec/elements/date_time_field_spec.rb +62 -57
  132. data/spec/watirspec/elements/date_time_fields_spec.rb +14 -15
  133. data/spec/watirspec/elements/dd_spec.rb +46 -48
  134. data/spec/watirspec/elements/dds_spec.rb +13 -15
  135. data/spec/watirspec/elements/del_spec.rb +27 -28
  136. data/spec/watirspec/elements/dels_spec.rb +13 -15
  137. data/spec/watirspec/elements/div_spec.rb +89 -91
  138. data/spec/watirspec/elements/divs_spec.rb +17 -19
  139. data/spec/watirspec/elements/dl_spec.rb +52 -54
  140. data/spec/watirspec/elements/dls_spec.rb +13 -15
  141. data/spec/watirspec/elements/dt_spec.rb +46 -48
  142. data/spec/watirspec/elements/dts_spec.rb +13 -15
  143. data/spec/watirspec/elements/element_spec.rb +240 -189
  144. data/spec/watirspec/elements/elements_spec.rb +16 -16
  145. data/spec/watirspec/elements/em_spec.rb +38 -40
  146. data/spec/watirspec/elements/ems_spec.rb +13 -15
  147. data/spec/watirspec/elements/filefield_spec.rb +45 -46
  148. data/spec/watirspec/elements/filefields_spec.rb +13 -15
  149. data/spec/watirspec/elements/font_spec.rb +11 -13
  150. data/spec/watirspec/elements/form_spec.rb +13 -15
  151. data/spec/watirspec/elements/forms_spec.rb +13 -15
  152. data/spec/watirspec/elements/frame_spec.rb +48 -50
  153. data/spec/watirspec/elements/frames_spec.rb +13 -15
  154. data/spec/watirspec/elements/hidden_spec.rb +23 -25
  155. data/spec/watirspec/elements/hiddens_spec.rb +13 -15
  156. data/spec/watirspec/elements/hn_spec.rb +22 -24
  157. data/spec/watirspec/elements/hns_spec.rb +13 -13
  158. data/spec/watirspec/elements/iframe_spec.rb +106 -74
  159. data/spec/watirspec/elements/iframes_spec.rb +16 -18
  160. data/spec/watirspec/elements/image_spec.rb +30 -32
  161. data/spec/watirspec/elements/images_spec.rb +13 -15
  162. data/spec/watirspec/elements/input_spec.rb +4 -5
  163. data/spec/watirspec/elements/ins_spec.rb +27 -29
  164. data/spec/watirspec/elements/inses_spec.rb +13 -15
  165. data/spec/watirspec/elements/label_spec.rb +17 -19
  166. data/spec/watirspec/elements/labels_spec.rb +13 -15
  167. data/spec/watirspec/elements/li_spec.rb +23 -25
  168. data/spec/watirspec/elements/link_spec.rb +45 -48
  169. data/spec/watirspec/elements/links_spec.rb +14 -16
  170. data/spec/watirspec/elements/lis_spec.rb +13 -15
  171. data/spec/watirspec/elements/list_spec.rb +14 -15
  172. data/spec/watirspec/elements/map_spec.rb +19 -20
  173. data/spec/watirspec/elements/maps_spec.rb +13 -15
  174. data/spec/watirspec/elements/meta_spec.rb +10 -10
  175. data/spec/watirspec/elements/metas_spec.rb +13 -15
  176. data/spec/watirspec/elements/ol_spec.rb +20 -21
  177. data/spec/watirspec/elements/ols_spec.rb +13 -15
  178. data/spec/watirspec/elements/option_spec.rb +63 -63
  179. data/spec/watirspec/elements/p_spec.rb +27 -26
  180. data/spec/watirspec/elements/pre_spec.rb +24 -25
  181. data/spec/watirspec/elements/pres_spec.rb +13 -15
  182. data/spec/watirspec/elements/ps_spec.rb +13 -15
  183. data/spec/watirspec/elements/radio_spec.rb +96 -97
  184. data/spec/watirspec/elements/radios_spec.rb +13 -15
  185. data/spec/watirspec/elements/select_list_spec.rb +244 -237
  186. data/spec/watirspec/elements/select_lists_spec.rb +15 -16
  187. data/spec/watirspec/elements/span_spec.rb +32 -31
  188. data/spec/watirspec/elements/spans_spec.rb +13 -15
  189. data/spec/watirspec/elements/strong_spec.rb +23 -24
  190. data/spec/watirspec/elements/strongs_spec.rb +13 -15
  191. data/spec/watirspec/elements/table_nesting_spec.rb +15 -14
  192. data/spec/watirspec/elements/table_spec.rb +61 -62
  193. data/spec/watirspec/elements/tables_spec.rb +15 -17
  194. data/spec/watirspec/elements/tbody_spec.rb +25 -26
  195. data/spec/watirspec/elements/tbodys_spec.rb +17 -19
  196. data/spec/watirspec/elements/td_spec.rb +20 -22
  197. data/spec/watirspec/elements/tds_spec.rb +9 -11
  198. data/spec/watirspec/elements/text_field_spec.rb +55 -56
  199. data/spec/watirspec/elements/text_fields_spec.rb +15 -16
  200. data/spec/watirspec/elements/textarea_spec.rb +2 -2
  201. data/spec/watirspec/elements/textareas_spec.rb +1 -1
  202. data/spec/watirspec/elements/tfoot_spec.rb +22 -23
  203. data/spec/watirspec/elements/tfoots_spec.rb +19 -19
  204. data/spec/watirspec/elements/thead_spec.rb +21 -23
  205. data/spec/watirspec/elements/theads_spec.rb +19 -19
  206. data/spec/watirspec/elements/tr_spec.rb +20 -22
  207. data/spec/watirspec/elements/trs_spec.rb +17 -19
  208. data/spec/watirspec/elements/ul_spec.rb +17 -19
  209. data/spec/watirspec/elements/uls_spec.rb +13 -14
  210. data/spec/watirspec/html/data_attributes.html +1 -0
  211. data/spec/watirspec/radio_set_spec.rb +100 -99
  212. data/spec/watirspec/relaxed_locate_spec.rb +19 -43
  213. data/spec/watirspec/screenshot_spec.rb +4 -4
  214. data/spec/watirspec/special_chars_spec.rb +2 -4
  215. data/spec/watirspec/support/rspec_matchers.rb +85 -22
  216. data/spec/watirspec/user_editable_spec.rb +84 -85
  217. data/spec/watirspec/wait_spec.rb +74 -95
  218. data/spec/watirspec/window_switching_spec.rb +131 -132
  219. data/spec/watirspec_helper.rb +12 -9
  220. data/support/travis.sh +4 -0
  221. data/support/version_differ.rb +12 -13
  222. data/watir.gemspec +29 -22
  223. metadata +76 -50
@@ -5,7 +5,7 @@ module Watir
5
5
  class XPath < Element::SelectorBuilder::XPath
6
6
  def lhs_for(building, key)
7
7
  if building == :input && key == :text
8
- "@value"
8
+ '@value'
9
9
  else
10
10
  super
11
11
  end
@@ -5,8 +5,8 @@ module Watir
5
5
  def validate(element, _selector)
6
6
  tag_name = element.tag_name.downcase
7
7
  return unless %w[input button].include?(tag_name)
8
- # TODO - Verify this is desired behavior based on https://bugzilla.mozilla.org/show_bug.cgi?id=1290963
9
- return if tag_name == "input" && !Watir::Button::VALID_TYPES.include?(element.attribute(:type).downcase)
8
+ # TODO: - Verify this is desired behavior based on https://bugzilla.mozilla.org/show_bug.cgi?id=1290963
9
+ return if tag_name == 'input' && !Watir::Button::VALID_TYPES.include?(element.attribute(:type).downcase)
10
10
 
11
11
  element
12
12
  end
@@ -2,13 +2,11 @@ module Watir
2
2
  module Locators
3
3
  class Cell
4
4
  class Locator < Element::Locator
5
-
6
5
  private
7
6
 
8
7
  def using_selenium(*)
9
8
  # force watir usage
10
9
  end
11
-
12
10
  end
13
11
  end
14
12
  end
@@ -3,16 +3,14 @@ module Watir
3
3
  class Cell
4
4
  class SelectorBuilder < Element::SelectorBuilder
5
5
  def build_wd_selector(selectors)
6
- return if selectors.values.any? { |e| e.kind_of? Regexp }
6
+ return if selectors.values.any? { |e| e.is_a? Regexp }
7
7
 
8
8
  expressions = %w[./th ./td]
9
9
  attr_expr = xpath_builder.attribute_expression(nil, selectors)
10
10
 
11
- unless attr_expr.empty?
12
- expressions.map! { |e| "#{e}[#{attr_expr}]" }
13
- end
11
+ expressions.map! { |e| "#{e}[#{attr_expr}]" } unless attr_expr.empty?
14
12
 
15
- xpath = expressions.join(" | ")
13
+ xpath = expressions.join(' | ')
16
14
 
17
15
  p build_wd_selector: xpath if $DEBUG
18
16
 
@@ -5,24 +5,24 @@ module Watir
5
5
  attr_reader :selector_builder
6
6
  attr_reader :element_validator
7
7
 
8
- W3C_FINDERS = [
9
- :css,
10
- :link,
11
- :link_text,
12
- :partial_link_text,
13
- :tag_name,
14
- :xpath
15
- ]
8
+ W3C_FINDERS = %i[
9
+ css
10
+ link
11
+ link_text
12
+ partial_link_text
13
+ tag_name
14
+ xpath
15
+ ].freeze
16
16
 
17
17
  # Regular expressions that can be reliably converted to xpath `contains`
18
18
  # expressions in order to optimize the locator.
19
- CONVERTABLE_REGEXP = %r{
19
+ CONVERTABLE_REGEXP = /
20
20
  \A
21
21
  ([^\[\]\\^$.|?*+()]*) # leading literal characters
22
22
  [^|]*? # do not try to convert expressions with alternates
23
23
  ([^\[\]\\^$.|?*+()]*) # trailing literal characters
24
24
  \z
25
- }x
25
+ /x
26
26
 
27
27
  def initialize(query_scope, selector, selector_builder, element_validator)
28
28
  @query_scope = query_scope # either element or browser
@@ -39,6 +39,7 @@ module Watir
39
39
 
40
40
  def locate_all
41
41
  return [@selector[:element]] if @selector.key?(:element)
42
+
42
43
  using_selenium(:all) || using_watir(:all)
43
44
  end
44
45
 
@@ -66,27 +67,18 @@ module Watir
66
67
  unless how
67
68
  raise Error, "internal error: unable to build Selenium selector from #{@normalized_selector.inspect}"
68
69
  end
70
+
69
71
  what = add_regexp_predicates(what) if how == :xpath
70
72
 
71
73
  if filter == :all || !@filter_selector.empty?
72
- retries = 0
73
- begin
74
- elements = locate_elements(how, what, @driver_scope) || []
75
- filter_elements(elements, filter: filter)
76
- rescue Selenium::WebDriver::Error::StaleElementReferenceError
77
- retries += 1
78
- sleep 0.5
79
- retry unless retries > 2
80
- target = filter == :all ? "element collection" : "element"
81
- raise StandardError, "Unable to locate #{target} from #{@selector} due to changing page"
82
- end
74
+ locate_filtered_elements(how, what, filter)
83
75
  else
84
76
  locate_element(how, what, @driver_scope)
85
77
  end
86
78
  end
87
79
 
88
80
  def validate(elements, tag_name)
89
- elements.compact.all? { |element| element_validator.validate(element, {tag_name: tag_name}) }
81
+ elements.compact.all? { |element| element_validator.validate(element, tag_name: tag_name) }
90
82
  end
91
83
 
92
84
  def fetch_value(element, how)
@@ -100,21 +92,19 @@ module Watir
100
92
  when :tag_name
101
93
  element.tag_name.downcase
102
94
  when :href
103
- (href = element.attribute(:href)) && href.strip
95
+ element.attribute('href')&.strip
96
+ when String, ::Symbol
97
+ how = how.to_s.tr('_', '-') if how.is_a?(::Symbol)
98
+ element.attribute(how)
104
99
  else
105
- element.attribute(how.to_s.tr("_", "-").to_sym)
100
+ raise Error::Exception, "Unable to fetch value for #{how}"
106
101
  end
107
102
  end
108
103
 
109
104
  def filter_elements(elements, filter: :first)
110
105
  selector = @filter_selector.dup
111
106
  if filter == :first
112
- idx = selector.delete(:index) || 0
113
- if idx < 0
114
- elements.reverse!
115
- idx = idx.abs - 1
116
- end
117
-
107
+ idx = element_index(elements, selector)
118
108
  counter = 0
119
109
 
120
110
  # Lazy evaluation to avoid fetching values for elements that will be discarded
@@ -122,18 +112,26 @@ module Watir
122
112
  counter += 1
123
113
  matches_selector?(el, selector)
124
114
  end
125
- val = matches.take(idx + 1).to_a[idx]
126
- Watir.logger.debug "Filtered through #{counter} elements to locate #{@selector.inspect}"
127
- val
115
+ msg = "Filtered through #{counter} elements to locate #{@selector.inspect}"
116
+ matches.take(idx + 1).to_a[idx].tap { Watir.logger.debug msg }
128
117
  else
129
118
  Watir.logger.debug "Iterated through #{elements.size} elements to locate all #{@selector.inspect}"
130
119
  elements.select { |el| matches_selector?(el, selector) }
131
120
  end
132
121
  end
133
122
 
123
+ def element_index(elements, selector)
124
+ idx = selector.delete(:index) || 0
125
+ return idx unless idx.negative?
126
+
127
+ elements.reverse!
128
+ idx.abs - 1
129
+ end
130
+
134
131
  def create_normalized_selector(filter)
135
132
  return @normalized_selector if @normalized_selector
136
- @driver_scope = ensure_scope_context
133
+
134
+ @driver_scope = @query_scope.wd
137
135
 
138
136
  @normalized_selector = selector_builder.normalized_selector
139
137
 
@@ -151,46 +149,56 @@ module Watir
151
149
  if @normalized_selector.key?(:index) && filter == :all
152
150
  raise ArgumentError, "can't locate all elements by :index"
153
151
  end
152
+
154
153
  @normalized_selector
155
154
  end
156
155
 
157
156
  def create_filter_selector
158
157
  return @filter_selector if @filter_selector
158
+
159
159
  @filter_selector = {}
160
160
 
161
161
  # Remove selectors that can never be used in XPath builder
162
- [:visible, :visible_text].each do |how|
162
+ %i[visible visible_text].each do |how|
163
163
  next unless @normalized_selector.key?(how)
164
+
164
165
  @filter_selector[how] = @normalized_selector.delete(how)
165
166
  end
166
167
 
167
- if tag_validation_required?(@normalized_selector)
168
- tag_name = @normalized_selector[:tag_name].is_a?(::Symbol) ? @normalized_selector[:tag_name].to_s : @normalized_selector[:tag_name]
169
- @filter_selector[:tag_name] = tag_name
170
- end
168
+ set_tag_validation if tag_validation_required?(@normalized_selector)
171
169
 
172
170
  # Regexp locators currently need to be validated even if they are included in the XPath builder
173
171
  # TODO: Identify Regexp that can have an exact equivalent using XPath contains (ie would not require
174
172
  # filtering) vs approximations (ie would still requiring filtering)
175
173
  @normalized_selector.each do |how, what|
176
174
  next unless what.is_a?(Regexp)
175
+
177
176
  @filter_selector[how] = @normalized_selector.delete(how)
178
177
  end
179
178
 
180
179
  if @normalized_selector[:index] && !@normalized_selector[:adjacent]
181
180
  idx = @normalized_selector.delete(:index)
182
181
 
183
- # Do not add {index: 0} filter if the only filter. This will allow using #find_element instead of #find_elements.
184
- implicit_idx_filter = @filter_selector.empty? && idx == 0
182
+ # Do not add {index: 0} filter if the only filter.
183
+ # This will allow using #find_element instead of #find_elements.
184
+ implicit_idx_filter = @filter_selector.empty? && idx.zero?
185
185
  @filter_selector[:index] = idx unless implicit_idx_filter
186
186
  end
187
187
 
188
188
  @filter_selector
189
189
  end
190
190
 
191
+ def set_tag_validation
192
+ @filter_selector[:tag_name] = if @normalized_selector[:tag_name].is_a?(::Symbol)
193
+ @normalized_selector[:tag_name].to_s
194
+ else
195
+ @normalized_selector[:tag_name]
196
+ end
197
+ end
198
+
191
199
  def process_label(label_key)
192
- regexp = @normalized_selector[label_key].kind_of?(Regexp)
193
- return unless (regexp || label_key == :visible_label) && selector_builder.should_use_label_element?
200
+ regexp = @normalized_selector[label_key].is_a?(Regexp)
201
+ return unless (regexp || label_key == :visible_label) && selector_builder.should_use_label_element?
194
202
 
195
203
  label = label_from_text(label_key)
196
204
  unless label # label not found, stop looking for element
@@ -218,9 +226,10 @@ module Watir
218
226
  def matches_selector?(element, selector)
219
227
  matches = selector.all? do |how, what|
220
228
  if how == :tag_name && what.is_a?(String)
221
- element_validator.validate(element, {tag_name: what})
229
+ element_validator.validate(element, tag_name: what)
222
230
  else
223
- what === fetch_value(element, how)
231
+ val = fetch_value(element, how)
232
+ what == val || val =~ /#{what}/
224
233
  end
225
234
  end
226
235
 
@@ -230,13 +239,15 @@ module Watir
230
239
  end
231
240
 
232
241
  def text_regexp_deprecation(element, selector, matches)
233
- text_content = Watir::Element.new(@query_scope, element: element).send(:execute_js, :getTextContent, element).strip
234
- text_content_matches = selector[:text] === text_content
235
- unless matches == text_content_matches
236
- key = @selector.key?(:text) ? "text" : "label"
237
- Watir.logger.deprecate("Using :#{key} locator with RegExp #{selector[:text].inspect} to match an element that includes hidden text", ":visible_#{key}",
238
- ids: [:text_regexp])
239
- end
242
+ new_element = Watir::Element.new(@query_scope, element: element)
243
+ text_content = new_element.execute_js(:getTextContent, element).strip
244
+ text_content_matches = text_content =~ /#{selector[:text]}/
245
+ return if matches == !!text_content_matches
246
+
247
+ key = @selector.key?(:text) ? 'text' : 'label'
248
+ selector_text = selector[:text].inspect
249
+ dep = "Using :#{key} locator with RegExp #{selector_text} to match an element that includes hidden text"
250
+ Watir.logger.deprecate(dep, ":visible_#{key}", ids: [:text_regexp])
240
251
  end
241
252
 
242
253
  def can_convert_regexp_to_contains?
@@ -247,20 +258,18 @@ module Watir
247
258
  return what unless can_convert_regexp_to_contains?
248
259
 
249
260
  @filter_selector.each do |key, value|
250
- next if [:tag_name, :text, :visible_text, :visible, :index].include?(key)
261
+ next if %i[tag_name text visible_text visible index].include?(key)
251
262
 
252
263
  predicates = regexp_selector_to_predicates(key, value)
253
- unless predicates.empty?
254
- what = "(#{what})[#{predicates.join(' and ')}]"
255
- end
264
+ what = "(#{what})[#{predicates.join(' and ')}]" unless predicates.empty?
256
265
  end
257
266
  what
258
267
  end
259
268
 
260
- def regexp_selector_to_predicates(key, re)
261
- return [] if re.casefold?
269
+ def regexp_selector_to_predicates(key, regexp)
270
+ return [] if regexp.casefold?
262
271
 
263
- match = re.source.match(CONVERTABLE_REGEXP)
272
+ match = regexp.source.match(CONVERTABLE_REGEXP)
264
273
  return [] unless match
265
274
 
266
275
  lhs = selector_builder.xpath_builder.lhs_for(nil, key)
@@ -273,10 +282,6 @@ module Watir
273
282
  (selector.key?(:css) || selector.key?(:xpath)) && selector.key?(:tag_name)
274
283
  end
275
284
 
276
- def ensure_scope_context
277
- @query_scope.wd
278
- end
279
-
280
285
  def locate_element(how, what, scope = @query_scope.wd)
281
286
  scope.find_element(how, what)
282
287
  end
@@ -285,12 +290,28 @@ module Watir
285
290
  scope.find_elements(how, what)
286
291
  end
287
292
 
293
+ def locate_filtered_elements(how, what, filter)
294
+ retries = 0
295
+ begin
296
+ elements = locate_elements(how, what, @driver_scope) || []
297
+ filter_elements(elements, filter: filter)
298
+ rescue Selenium::WebDriver::Error::StaleElementReferenceError
299
+ retries += 1
300
+ sleep 0.5
301
+ retry unless retries > 2
302
+ target = filter == :all ? 'element collection' : 'element'
303
+ raise StandardError, "Unable to locate #{target} from #{@selector} due to changing page"
304
+ end
305
+ end
306
+
288
307
  def wd_supported?(how, what, tag)
289
308
  return false unless W3C_FINDERS.include? how
290
- return false unless what.kind_of?(String)
309
+ return false unless what.is_a?(String)
310
+
291
311
  if %i[partial_link_text link_text link].include?(how)
292
312
  Watir.logger.deprecate(":#{how} locator", ':visible_text', ids: [:visible_text])
293
313
  return true if [:a, :link, nil].include?(tag)
314
+
294
315
  raise StandardError, "Can not use #{how} locator to find a #{what} element"
295
316
  elsif how == :tag_name
296
317
  return true
@@ -30,27 +30,19 @@ module Watir
30
30
  def check_type(how, what)
31
31
  case how
32
32
  when :index
33
- unless what.is_a?(Integer)
34
- raise TypeError, "expected Integer, got #{what.inspect}:#{what.class}"
35
- end
33
+ raise_unless_int(what)
36
34
  when :visible
37
- unless what.is_a?(TrueClass) || what.is_a?(FalseClass)
38
- raise TypeError, "expected TrueClass or FalseClass, got #{what.inspect}:#{what.class}"
39
- end
35
+ raise_unless_boolean(what)
40
36
  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
+ raise_unless_str_regex(what)
44
38
  else
45
- if what.is_a?(Array) && how != :class && how != :class_name
46
- raise TypeError, "Only :class locator can have a value of an Array"
47
- end
48
- if what.is_a?(Symbol) && how != :adjacent
49
- raise TypeError, "Symbol is not a valid value"
50
- end
51
- unless VALID_WHATS.any? { |t| what.is_a? t }
52
- raise TypeError, "expected one of #{VALID_WHATS.inspect}, got #{what.inspect}:#{what.class}"
39
+ if what.is_a?(Array) && !%i[class class_name].include?(how)
40
+ raise TypeError, 'Only :class locator can have a value of an Array'
53
41
  end
42
+ raise TypeError, 'Symbol is not a valid value' if what.is_a?(Symbol) && how != :adjacent
43
+ return if VALID_WHATS.any? { |t| what.is_a? t }
44
+
45
+ raise TypeError, "expected one of #{VALID_WHATS.inspect}, got #{what.inspect}:#{what.class}"
54
46
  end
55
47
  end
56
48
 
@@ -61,6 +53,7 @@ module Watir
61
53
  def build(selector)
62
54
  inspect = selector.inspect
63
55
  return given_xpath_or_css(selector) if selector.key?(:xpath) || selector.key?(:css)
56
+
64
57
  built = build_wd_selector(selector)
65
58
  Watir.logger.debug "Converted #{inspect} to #{built}"
66
59
  built
@@ -90,47 +83,39 @@ module Watir
90
83
 
91
84
  def check_custom_attribute(attribute)
92
85
  return if valid_attribute?(attribute) || attribute.to_s =~ WILDCARD_ATTRIBUTE
86
+
93
87
  @custom_attributes << attribute.to_s
94
88
  end
95
89
 
96
90
  def given_xpath_or_css(selector)
97
- xpath = selector.delete(:xpath)
98
- css = selector.delete(:css)
99
- return unless xpath || css
91
+ locator = {}
92
+ locator[:xpath] = selector.delete(:xpath) if selector.key?(:xpath)
93
+ locator[:css] = selector.delete(:css) if selector.key?(:css)
100
94
 
101
- if xpath && css
102
- raise ArgumentError, ":xpath and :css cannot be combined (#{selector.inspect})"
103
- end
95
+ return if locator.empty?
96
+ raise ArgumentError, ":xpath and :css cannot be combined (#{selector.inspect})" if locator.size > 1
104
97
 
105
- how, what = if xpath
106
- [:xpath, xpath]
107
- elsif css
108
- [:css, css]
109
- end
110
-
111
- if selector.any? && !can_be_combined_with_xpath_or_css?(selector)
112
- raise ArgumentError, "#{how} cannot be combined with other selectors (#{selector.inspect})"
113
- end
98
+ return locator.first unless selector.any? && !can_be_combined_with_xpath_or_css?(selector)
114
99
 
115
- [how, what]
100
+ msg = "#{locator.keys.first} cannot be combined with other selectors (#{selector.inspect})"
101
+ raise ArgumentError, msg
116
102
  end
117
103
 
118
104
  def build_wd_selector(selectors)
119
105
  return if selectors.values.any? { |e| e.is_a? Regexp }
106
+
120
107
  build_xpath(selectors)
121
108
  end
122
109
 
123
110
  def valid_attribute?(attribute)
124
- @valid_attributes && @valid_attributes.include?(attribute)
111
+ @valid_attributes&.include?(attribute)
125
112
  end
126
113
 
127
114
  def can_be_combined_with_xpath_or_css?(selector)
128
115
  keys = selector.keys
129
116
  return true if keys == [:tag_name]
130
117
 
131
- if selector[:tag_name] == "input"
132
- return keys.sort == [:tag_name, :type]
133
- end
118
+ return keys.sort == %i[tag_name type] if selector[:tag_name] == 'input'
134
119
 
135
120
  false
136
121
  end
@@ -141,10 +126,27 @@ module Watir
141
126
 
142
127
  def xpath_builder_class
143
128
  Kernel.const_get("#{self.class.name}::XPath")
144
- rescue
129
+ rescue StandardError
145
130
  XPath
146
131
  end
147
132
 
133
+ def raise_unless_int(what)
134
+ return if what.is_a?(Integer)
135
+
136
+ raise TypeError, "expected Integer, got #{what.inspect}:#{what.class}"
137
+ end
138
+
139
+ def raise_unless_boolean(what)
140
+ return if what.is_a?(TrueClass) || what.is_a?(FalseClass)
141
+
142
+ raise TypeError, "expected TrueClass or FalseClass, got #{what.inspect}:#{what.class}"
143
+ end
144
+
145
+ def raise_unless_str_regex(what)
146
+ return if what.is_a?(String) || what.is_a?(Regexp)
147
+
148
+ raise TypeError, "expected String or Regexp, got #{what.inspect}:#{what.class}"
149
+ end
148
150
  end
149
151
  end
150
152
  end