watir 7.1.0 → 7.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (253) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/chrome.yml +29 -16
  3. data/.github/workflows/edge.yml +19 -11
  4. data/.github/workflows/firefox.yml +28 -16
  5. data/.github/workflows/ie.yml +13 -7
  6. data/.github/workflows/safari.yml +22 -11
  7. data/.github/workflows/unit.yml +31 -23
  8. data/.rubocop.yml +14 -7
  9. data/.rubocop_todo.yml +0 -26
  10. data/CHANGES.md +16 -1
  11. data/Gemfile +6 -1
  12. data/README.md +15 -11
  13. data/Rakefile +9 -7
  14. data/lib/watir/adjacent.rb +3 -1
  15. data/lib/watir/after_hooks.rb +5 -3
  16. data/lib/watir/alert.rb +2 -0
  17. data/lib/watir/aliases.rb +2 -0
  18. data/lib/watir/attribute_helper.rb +4 -2
  19. data/lib/watir/browser.rb +10 -14
  20. data/lib/watir/capabilities.rb +56 -33
  21. data/lib/watir/cell_container.rb +2 -0
  22. data/lib/watir/container.rb +2 -0
  23. data/lib/watir/cookies.rb +5 -5
  24. data/lib/watir/element_collection.rb +3 -1
  25. data/lib/watir/elements/button.rb +2 -0
  26. data/lib/watir/elements/cell.rb +2 -0
  27. data/lib/watir/elements/checkbox.rb +2 -0
  28. data/lib/watir/elements/date_field.rb +2 -0
  29. data/lib/watir/elements/date_time_field.rb +2 -0
  30. data/lib/watir/elements/dlist.rb +3 -1
  31. data/lib/watir/elements/element.rb +29 -105
  32. data/lib/watir/elements/file_field.rb +2 -0
  33. data/lib/watir/elements/font.rb +2 -0
  34. data/lib/watir/elements/form.rb +2 -0
  35. data/lib/watir/elements/hidden.rb +2 -0
  36. data/lib/watir/elements/iframe.rb +2 -0
  37. data/lib/watir/elements/image.rb +2 -0
  38. data/lib/watir/elements/input.rb +2 -0
  39. data/lib/watir/elements/link.rb +2 -0
  40. data/lib/watir/elements/list.rb +2 -0
  41. data/lib/watir/elements/option.rb +2 -0
  42. data/lib/watir/elements/radio.rb +2 -0
  43. data/lib/watir/elements/row.rb +2 -0
  44. data/lib/watir/elements/select.rb +2 -0
  45. data/lib/watir/elements/table.rb +3 -1
  46. data/lib/watir/elements/table_cell.rb +2 -0
  47. data/lib/watir/elements/table_row.rb +2 -0
  48. data/lib/watir/elements/table_section.rb +2 -0
  49. data/lib/watir/elements/text_area.rb +2 -0
  50. data/lib/watir/elements/text_field.rb +2 -0
  51. data/lib/watir/exception.rb +2 -0
  52. data/lib/watir/extensions/nokogiri.rb +2 -0
  53. data/lib/watir/generator/base/generator.rb +2 -0
  54. data/lib/watir/generator/base/idl_sorter.rb +3 -1
  55. data/lib/watir/generator/base/spec_extractor.rb +6 -4
  56. data/lib/watir/generator/base/util.rb +2 -0
  57. data/lib/watir/generator/base/visitor.rb +5 -5
  58. data/lib/watir/generator/base.rb +2 -1
  59. data/lib/watir/generator/html/generator.rb +2 -0
  60. data/lib/watir/generator/html/spec_extractor.rb +2 -0
  61. data/lib/watir/generator/html/visitor.rb +2 -0
  62. data/lib/watir/generator/html.rb +2 -0
  63. data/lib/watir/generator/svg/generator.rb +2 -0
  64. data/lib/watir/generator/svg/spec_extractor.rb +2 -0
  65. data/lib/watir/generator/svg/visitor.rb +2 -0
  66. data/lib/watir/generator/svg.rb +2 -0
  67. data/lib/watir/generator.rb +2 -0
  68. data/lib/watir/has_window.rb +3 -1
  69. data/lib/watir/http_client.rb +2 -0
  70. data/lib/watir/js_execution.rb +2 -0
  71. data/lib/watir/js_snippets.rb +2 -0
  72. data/lib/watir/locators/anchor/selector_builder.rb +2 -0
  73. data/lib/watir/locators/button/matcher.rb +2 -0
  74. data/lib/watir/locators/button/selector_builder/xpath.rb +15 -19
  75. data/lib/watir/locators/button/selector_builder.rb +2 -0
  76. data/lib/watir/locators/cell/selector_builder/xpath.rb +2 -0
  77. data/lib/watir/locators/cell/selector_builder.rb +2 -0
  78. data/lib/watir/locators/element/locator.rb +2 -0
  79. data/lib/watir/locators/element/matcher.rb +2 -0
  80. data/lib/watir/locators/element/selector_builder/regexp_disassembler.rb +2 -0
  81. data/lib/watir/locators/element/selector_builder/xpath.rb +16 -15
  82. data/lib/watir/locators/element/selector_builder/xpath_support.rb +4 -2
  83. data/lib/watir/locators/element/selector_builder.rb +16 -6
  84. data/lib/watir/locators/option/matcher.rb +2 -0
  85. data/lib/watir/locators/option/selector_builder/xpath.rb +2 -0
  86. data/lib/watir/locators/option/selector_builder.rb +2 -0
  87. data/lib/watir/locators/row/selector_builder/xpath.rb +2 -0
  88. data/lib/watir/locators/row/selector_builder.rb +2 -0
  89. data/lib/watir/locators/text_area/selector_builder/xpath.rb +2 -0
  90. data/lib/watir/locators/text_area/selector_builder.rb +2 -0
  91. data/lib/watir/locators/text_field/matcher.rb +2 -0
  92. data/lib/watir/locators/text_field/selector_builder/xpath.rb +2 -0
  93. data/lib/watir/locators/text_field/selector_builder.rb +2 -0
  94. data/lib/watir/locators.rb +2 -0
  95. data/lib/watir/logger.rb +2 -0
  96. data/lib/watir/navigation.rb +3 -1
  97. data/lib/watir/radio_set.rb +2 -0
  98. data/lib/watir/row_container.rb +2 -0
  99. data/lib/watir/screenshot.rb +2 -0
  100. data/lib/watir/scroll.rb +27 -2
  101. data/lib/watir/search_context.rb +96 -0
  102. data/lib/watir/shadow_root.rb +60 -0
  103. data/lib/watir/user_editable.rb +3 -1
  104. data/lib/watir/version.rb +3 -1
  105. data/lib/watir/wait/timer.rb +3 -1
  106. data/lib/watir/wait.rb +5 -5
  107. data/lib/watir/window.rb +7 -3
  108. data/lib/watir/window_collection.rb +4 -1
  109. data/lib/watir.rb +4 -0
  110. data/lib/watirspec/guards.rb +2 -0
  111. data/lib/watirspec/implementation.rb +14 -10
  112. data/lib/watirspec/rake_tasks.rb +4 -4
  113. data/lib/watirspec/remote_server.rb +5 -3
  114. data/lib/watirspec/runner.rb +3 -1
  115. data/lib/watirspec/server/app.rb +2 -0
  116. data/lib/watirspec/server.rb +2 -0
  117. data/lib/watirspec.rb +7 -4
  118. data/spec/locator_spec_helper.rb +2 -0
  119. data/spec/spec_helper.rb +2 -0
  120. data/spec/unit/capabilities_spec.rb +77 -58
  121. data/spec/unit/element_locator_spec.rb +2 -0
  122. data/spec/unit/match_elements/button_spec.rb +2 -0
  123. data/spec/unit/match_elements/element_spec.rb +10 -8
  124. data/spec/unit/match_elements/text_field_spec.rb +2 -0
  125. data/spec/unit/selector_builder/anchor_spec.rb +2 -0
  126. data/spec/unit/selector_builder/button_spec.rb +31 -28
  127. data/spec/unit/selector_builder/cell_spec.rb +3 -1
  128. data/spec/unit/selector_builder/element_spec.rb +61 -60
  129. data/spec/unit/selector_builder/row_spec.rb +21 -10
  130. data/spec/unit/selector_builder/text_field_spec.rb +29 -21
  131. data/spec/unit/selector_builder/textarea_spec.rb +2 -0
  132. data/spec/unit/unit_helper.rb +2 -0
  133. data/spec/unit/wait_spec.rb +2 -0
  134. data/spec/watirspec/adjacent_spec.rb +8 -6
  135. data/spec/watirspec/after_hooks_spec.rb +19 -21
  136. data/spec/watirspec/alert_spec.rb +2 -0
  137. data/spec/watirspec/attributes_spec.rb +2 -0
  138. data/spec/watirspec/browser_spec.rb +16 -12
  139. data/spec/watirspec/capabilities_spec.rb +569 -0
  140. data/spec/watirspec/cookies_spec.rb +4 -2
  141. data/spec/watirspec/drag_and_drop_spec.rb +2 -0
  142. data/spec/watirspec/element_hidden_spec.rb +2 -0
  143. data/spec/watirspec/elements/area_spec.rb +2 -0
  144. data/spec/watirspec/elements/areas_spec.rb +2 -0
  145. data/spec/watirspec/elements/button_spec.rb +2 -0
  146. data/spec/watirspec/elements/buttons_spec.rb +2 -0
  147. data/spec/watirspec/elements/checkbox_spec.rb +2 -0
  148. data/spec/watirspec/elements/checkboxes_spec.rb +2 -0
  149. data/spec/watirspec/elements/collections_spec.rb +5 -3
  150. data/spec/watirspec/elements/date_field_spec.rb +2 -0
  151. data/spec/watirspec/elements/date_fields_spec.rb +2 -0
  152. data/spec/watirspec/elements/date_time_field_spec.rb +2 -0
  153. data/spec/watirspec/elements/date_time_fields_spec.rb +2 -0
  154. data/spec/watirspec/elements/dd_spec.rb +2 -0
  155. data/spec/watirspec/elements/dds_spec.rb +2 -0
  156. data/spec/watirspec/elements/del_spec.rb +2 -0
  157. data/spec/watirspec/elements/dels_spec.rb +2 -0
  158. data/spec/watirspec/elements/div_spec.rb +2 -1
  159. data/spec/watirspec/elements/divs_spec.rb +2 -0
  160. data/spec/watirspec/elements/dl_spec.rb +6 -6
  161. data/spec/watirspec/elements/dls_spec.rb +2 -0
  162. data/spec/watirspec/elements/dt_spec.rb +2 -0
  163. data/spec/watirspec/elements/dts_spec.rb +2 -0
  164. data/spec/watirspec/elements/element_spec.rb +9 -1
  165. data/spec/watirspec/elements/elements_spec.rb +2 -0
  166. data/spec/watirspec/elements/em_spec.rb +2 -0
  167. data/spec/watirspec/elements/ems_spec.rb +2 -0
  168. data/spec/watirspec/elements/filefield_spec.rb +2 -0
  169. data/spec/watirspec/elements/filefields_spec.rb +2 -0
  170. data/spec/watirspec/elements/font_spec.rb +2 -0
  171. data/spec/watirspec/elements/form_spec.rb +2 -0
  172. data/spec/watirspec/elements/forms_spec.rb +2 -0
  173. data/spec/watirspec/elements/frame_spec.rb +2 -0
  174. data/spec/watirspec/elements/frames_spec.rb +2 -0
  175. data/spec/watirspec/elements/hidden_spec.rb +2 -0
  176. data/spec/watirspec/elements/hiddens_spec.rb +2 -0
  177. data/spec/watirspec/elements/hn_spec.rb +2 -0
  178. data/spec/watirspec/elements/hns_spec.rb +2 -0
  179. data/spec/watirspec/elements/iframe_spec.rb +3 -1
  180. data/spec/watirspec/elements/iframes_spec.rb +2 -0
  181. data/spec/watirspec/elements/image_spec.rb +2 -0
  182. data/spec/watirspec/elements/images_spec.rb +2 -0
  183. data/spec/watirspec/elements/input_spec.rb +2 -0
  184. data/spec/watirspec/elements/ins_spec.rb +2 -0
  185. data/spec/watirspec/elements/inses_spec.rb +2 -0
  186. data/spec/watirspec/elements/label_spec.rb +2 -0
  187. data/spec/watirspec/elements/labels_spec.rb +2 -0
  188. data/spec/watirspec/elements/li_spec.rb +2 -0
  189. data/spec/watirspec/elements/link_spec.rb +4 -4
  190. data/spec/watirspec/elements/links_spec.rb +2 -0
  191. data/spec/watirspec/elements/lis_spec.rb +2 -0
  192. data/spec/watirspec/elements/list_spec.rb +2 -0
  193. data/spec/watirspec/elements/map_spec.rb +2 -0
  194. data/spec/watirspec/elements/maps_spec.rb +2 -0
  195. data/spec/watirspec/elements/meta_spec.rb +2 -0
  196. data/spec/watirspec/elements/metas_spec.rb +2 -0
  197. data/spec/watirspec/elements/ol_spec.rb +2 -0
  198. data/spec/watirspec/elements/ols_spec.rb +2 -0
  199. data/spec/watirspec/elements/option_spec.rb +2 -0
  200. data/spec/watirspec/elements/p_spec.rb +2 -0
  201. data/spec/watirspec/elements/pre_spec.rb +2 -0
  202. data/spec/watirspec/elements/pres_spec.rb +2 -0
  203. data/spec/watirspec/elements/ps_spec.rb +2 -0
  204. data/spec/watirspec/elements/radio_spec.rb +2 -0
  205. data/spec/watirspec/elements/radios_spec.rb +2 -0
  206. data/spec/watirspec/elements/select_list_spec.rb +7 -1
  207. data/spec/watirspec/elements/select_lists_spec.rb +2 -0
  208. data/spec/watirspec/elements/span_spec.rb +2 -0
  209. data/spec/watirspec/elements/spans_spec.rb +2 -0
  210. data/spec/watirspec/elements/strong_spec.rb +2 -0
  211. data/spec/watirspec/elements/strongs_spec.rb +2 -0
  212. data/spec/watirspec/elements/table_nesting_spec.rb +2 -0
  213. data/spec/watirspec/elements/table_spec.rb +2 -0
  214. data/spec/watirspec/elements/tables_spec.rb +2 -0
  215. data/spec/watirspec/elements/tbody_spec.rb +2 -0
  216. data/spec/watirspec/elements/tbodys_spec.rb +2 -0
  217. data/spec/watirspec/elements/td_spec.rb +2 -0
  218. data/spec/watirspec/elements/tds_spec.rb +2 -0
  219. data/spec/watirspec/elements/text_field_spec.rb +2 -0
  220. data/spec/watirspec/elements/text_fields_spec.rb +2 -0
  221. data/spec/watirspec/elements/textarea_spec.rb +2 -0
  222. data/spec/watirspec/elements/textareas_spec.rb +2 -0
  223. data/spec/watirspec/elements/tfoot_spec.rb +2 -0
  224. data/spec/watirspec/elements/tfoots_spec.rb +2 -0
  225. data/spec/watirspec/elements/thead_spec.rb +2 -0
  226. data/spec/watirspec/elements/theads_spec.rb +2 -0
  227. data/spec/watirspec/elements/tr_spec.rb +2 -0
  228. data/spec/watirspec/elements/trs_spec.rb +2 -0
  229. data/spec/watirspec/elements/ul_spec.rb +2 -0
  230. data/spec/watirspec/elements/uls_spec.rb +2 -0
  231. data/spec/watirspec/html/child_frame.html +29 -0
  232. data/spec/watirspec/html/class_locator.html +2 -0
  233. data/spec/watirspec/html/scroll_nested.html +17 -0
  234. data/spec/watirspec/html/scroll_nested_offscreen.html +18 -0
  235. data/spec/watirspec/html/shadow_dom.html +28 -0
  236. data/spec/watirspec/radio_set_spec.rb +2 -0
  237. data/spec/watirspec/screenshot_spec.rb +4 -2
  238. data/spec/watirspec/scroll_spec.rb +75 -7
  239. data/spec/watirspec/shadow_root_spec.rb +103 -0
  240. data/spec/watirspec/special_chars_spec.rb +2 -0
  241. data/spec/watirspec/support/rspec_matchers.rb +35 -32
  242. data/spec/watirspec/user_editable_spec.rb +2 -0
  243. data/spec/watirspec/wait_spec.rb +2 -0
  244. data/spec/watirspec/window_switching_spec.rb +48 -38
  245. data/spec/watirspec_helper.rb +24 -18
  246. data/support/doctest_helper.rb +2 -0
  247. data/support/version_differ.rb +2 -0
  248. data/watir.gemspec +11 -8
  249. metadata +54 -215
  250. data/.github/actions/enable-safari/action.yml +0 -11
  251. data/.github/actions/install-chrome/action.yml +0 -12
  252. data/.github/actions/setup-linux/action.yml +0 -8
  253. data/lib/watir-webdriver.rb +0 -2
data/lib/watir/aliases.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  module Container
3
5
  alias field_set fieldset
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  #
3
5
  # @private
@@ -29,9 +31,9 @@ module Watir
29
31
 
30
32
  def attribute_list
31
33
  @attribute_list ||= (typed_attributes.values.flatten +
32
- ancestors[1..].map { |e|
34
+ ancestors[1..].filter_map { |e|
33
35
  e.attribute_list if e.respond_to?(:attribute_list)
34
- }.compact.flatten
36
+ }.flatten
35
37
  ).uniq
36
38
  end
37
39
 
data/lib/watir/browser.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  #
3
5
  # The main class through which you control the browser.
@@ -40,15 +42,8 @@ module Watir
40
42
  #
41
43
 
42
44
  def initialize(browser = :chrome, *args)
43
- case browser
44
- when ::Symbol, String
45
- @capabilities = Capabilities.new(browser, *args)
46
- @driver = Selenium::WebDriver.for(*@capabilities.to_args)
47
- when Selenium::WebDriver::Driver
48
- @driver = browser
49
- else
50
- raise ArgumentError, "expected Symbol or Selenium::WebDriver::Driver, got #{browser.class}"
51
- end
45
+ @capabilities = browser.is_a?(Capabilities) ? browser : Capabilities.new(browser, *args)
46
+ @driver = browser.is_a?(Selenium::WebDriver::Driver) ? browser : Selenium::WebDriver.for(*@capabilities.to_args)
52
47
 
53
48
  @after_hooks = AfterHooks.new(self)
54
49
  @closed = false
@@ -56,17 +51,18 @@ module Watir
56
51
  end
57
52
 
58
53
  # rubocop:disable Metrics/AbcSize
59
- # TODO: w3c default behavior does not like checking if alert exists
60
54
  def inspect
61
55
  if alert.exists?
62
- format('#<%s:0x%x alert=true>', self.class, hash * 2)
56
+ format('#<%<class>s:0x%<hash>x alert=true>', class: self.class, hash: hash * 2)
63
57
  else
64
- format('#<%s:0x%x url=%s title=%s>', self.class, hash * 2, url.inspect, title.inspect)
58
+ format('#<%<class>s:0x%<hash>x url=%<url>s title=%<title>s>',
59
+ class: self.class, hash: hash * 2, url: url.inspect, title: title.inspect)
65
60
  end
66
61
  rescue Selenium::WebDriver::Error::NoSuchWindowError
67
- format('#<%s:0x%x closed=%s>', self.class, hash * 2, closed?)
62
+ format('#<%<class>s:0x%<hash>x closed=%<closed>s>',
63
+ class: self.class, hash: hash * 2, closed: closed?)
68
64
  rescue Errno::ECONNREFUSED
69
- format('#<%s:0x%x closed=true>', self.class, hash * 2)
65
+ format('#<%<class>s:0x%<hash>x closed=true>', class: self.class, hash: hash * 2)
70
66
  end
71
67
  alias selector_string inspect
72
68
  # rubocop:enable Metrics/AbcSize
@@ -1,28 +1,35 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  class Capabilities
3
- attr_reader :options
5
+ attr_reader :options, :selenium_browser, :selenium_args
4
6
 
5
7
  def initialize(browser = nil, options = {})
6
- if browser.is_a?(Hash)
7
- options = browser
8
- browser = nil
9
- end
10
-
11
- @options = options.dup
12
- Watir.logger.info "Creating Browser instance of #{browser} with user provided options: #{@options.inspect}"
13
-
14
- if @options.key?(:capabilities) && @options.key?(:options)
15
- raise(ArgumentError, ':capabilities and :options are not both allowed')
16
- end
17
- raise(ArgumentError, ':url and :service are not both allowed') if @options.key?(:service) && @options.key?(:url)
18
-
19
- @browser = browser.nil? && infer_browser || browser
8
+ @options, @browser = case browser
9
+ when Selenium::WebDriver::Driver
10
+ return
11
+ when ::Symbol, String
12
+ [options.dup, browser&.to_sym]
13
+ when Hash
14
+ [browser.dup, infer_browser(browser)]
15
+ when nil
16
+ [{}, infer_browser]
17
+ else
18
+ raise ArgumentError,
19
+ "expected Driver, String, Symbol or Hash, but received: #{browser.class}"
20
+ end
21
+ validate_options
20
22
 
21
23
  @selenium_browser = @options.key?(:url) ? :remote : @browser
22
24
  end
23
25
 
24
26
  def to_args
25
- [@selenium_browser, process_arguments]
27
+ Watir.logger.info "Creating Browser instance of #{@browser} with user provided options: #{@options.inspect}"
28
+ @selenium_args = process_arguments
29
+ raise ArgumentError, "#{@options} are unrecognized arguments for Browser constructor" unless @options.empty?
30
+
31
+ Watir.logger.info "Selenium options generated by Watir: #{@selenium_args.inspect}"
32
+ [@selenium_browser, @selenium_args]
26
33
  end
27
34
 
28
35
  private
@@ -39,9 +46,15 @@ module Watir
39
46
  end
40
47
 
41
48
  selenium_opts[:http_client] = process_http_client
42
- selenium_opts[:capabilities] = @options.delete(:capabilities) || process_browser_options
49
+ if @options.key?(:capabilities)
50
+ Watir.logger.deprecate(':capabilities argument in Browser constructor',
51
+ ':options argument with Selenium Options instance or Hash',
52
+ id: :capabilities)
53
+ selenium_opts[:capabilities] = @options.delete(:capabilities)
54
+ else
55
+ selenium_opts[:options] = process_browser_options
56
+ end
43
57
 
44
- Watir.logger.info "Creating Browser instance with Watir processed options: #{selenium_opts.inspect}"
45
58
  selenium_opts
46
59
  end
47
60
 
@@ -64,7 +77,6 @@ module Watir
64
77
 
65
78
  def process_browser_options
66
79
  browser_options = @options.delete(:options).dup || {}
67
- vendor_caps = process_vendor_capabilities(browser_options)
68
80
 
69
81
  options = if browser_options.is_a? Selenium::WebDriver::Options
70
82
  browser_options
@@ -76,9 +88,11 @@ module Watir
76
88
  options.unhandled_prompt_behavior ||= :ignore
77
89
  process_proxy_options(options)
78
90
  browser_specific_options(options)
79
- raise ArgumentError, "#{@options} are unrecognized arguments for Browser constructor" unless @options.empty?
80
91
 
81
- vendor_caps << options
92
+ vendor_opts = process_vendor_options(browser_options)
93
+ vendor_opts.each { |opts| options.add_option(opts) }
94
+
95
+ options
82
96
  end
83
97
 
84
98
  def process_proxy_options(options)
@@ -94,7 +108,7 @@ module Watir
94
108
  options.proxy = proxy
95
109
  end
96
110
 
97
- def process_vendor_capabilities(opts)
111
+ def process_vendor_options(opts)
98
112
  return [] unless opts.is_a? Hash
99
113
 
100
114
  vendor = opts.select { |key, _val| key.to_s.include?(':') && opts.delete(key) }
@@ -103,12 +117,12 @@ module Watir
103
117
 
104
118
  def convert_timeouts(browser_options)
105
119
  browser_options[:timeouts] ||= {}
106
- browser_options[:timeouts].keys.each do |key|
120
+ browser_options[:timeouts].each_key do |key|
107
121
  raise(ArgumentError, 'do not set implicit wait, Watir handles waiting automatically') if key.to_s == 'implicit'
108
122
 
109
123
  Watir.logger.deprecate('using timeouts directly in options',
110
124
  ":#{key}_timeout",
111
- id: 'timeouts')
125
+ id: :timeouts)
112
126
  end
113
127
  if browser_options.key?(:page_load_timeout)
114
128
  browser_options[:timeouts][:page_load] = browser_options.delete(:page_load_timeout) * 1000
@@ -151,16 +165,25 @@ module Watir
151
165
  end
152
166
  end
153
167
 
154
- def infer_browser
155
- if @options.key?(:browser)
156
- @options.delete(:browser)
157
- elsif @options.key?(:capabilities)
158
- @options[:capabilities].browser_name.tr(' ', '_').downcase.to_sym
159
- elsif @options.key?(:options)
160
- @options[:options].class.to_s.split('::')[-2].downcase.to_sym
161
- else
162
- :chrome
168
+ def infer_browser(options = nil)
169
+ options ||= {}
170
+ inferred = if options.key?(:capabilities)
171
+ options[:capabilities].browser_name.tr(' ', '_').downcase.to_sym
172
+ elsif options[:options].is_a?(Selenium::WebDriver::Options)
173
+ options[:options].class.to_s.split('::')[-2].downcase.to_sym
174
+ elsif options.key?(:options)
175
+ options.dig(:options, :browser_name).downcase.to_sym
176
+ else
177
+ :chrome
178
+ end
179
+ %i[msedge microsoftedge].include?(inferred) ? :edge : inferred
180
+ end
181
+
182
+ def validate_options
183
+ if @options.key?(:capabilities) && @options.key?(:options)
184
+ raise(ArgumentError, ':capabilities and :options are not both allowed')
163
185
  end
186
+ raise(ArgumentError, ':url and :service are not both allowed') if @options.key?(:service) && @options.key?(:url)
164
187
  end
165
188
  end
166
189
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  module CellContainer
3
5
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  module Container
3
5
  include JSSnippets
data/lib/watir/cookies.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'yaml'
2
4
 
3
5
  module Watir
@@ -18,7 +20,7 @@ module Watir
18
20
 
19
21
  def to_a
20
22
  @control.all_cookies.map do |e|
21
- e.merge(expires: e[:expires] ? e[:expires].to_time : nil)
23
+ e.merge(expires: e[:expires]&.to_time)
22
24
  end
23
25
  end
24
26
 
@@ -102,11 +104,9 @@ module Watir
102
104
  #
103
105
 
104
106
  def save(file = '.cookies')
105
- IO.write(file, to_a.to_yaml)
107
+ File.write(file, to_a.to_yaml)
106
108
  end
107
109
 
108
- #
109
- # TODO: Use :permitted_classes keyword when minimum supported Ruby is 2.6
110
110
  #
111
111
  # Load cookies from file
112
112
  #
@@ -117,7 +117,7 @@ module Watir
117
117
  #
118
118
 
119
119
  def load(file = '.cookies')
120
- YAML.safe_load(IO.read(file), [::Symbol, ::Time]).each do |c|
120
+ YAML.safe_load(File.read(file), permitted_classes: [::Symbol, ::Time]).each do |c|
121
121
  add(c.delete(:name), c.delete(:value), c)
122
122
  end
123
123
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  #
3
5
  # Base class for element collections.
@@ -193,7 +195,7 @@ module Watir
193
195
  end
194
196
 
195
197
  def ensure_context
196
- if @query_scope.is_a?(Browser) || !@query_scope.located? && @query_scope.is_a?(IFrame)
198
+ if @query_scope.is_a?(Browser) || (!@query_scope.located? && @query_scope.is_a?(IFrame))
197
199
  @query_scope.browser.locate
198
200
  elsif @query_scope.located? && @query_scope.stale?
199
201
  @query_scope.locate
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  #
3
5
  # Class representing button elements.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  #
3
5
  # Custom class representing table cell (th or td).
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  class CheckBox < Input
3
5
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  class DateField < Input
3
5
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  class DateTimeField < Input
3
5
  #
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  class DList < HTMLElement
3
5
  def to_hash
4
6
  keys = dts.map(&:text)
5
7
  values = dds.map(&:text)
6
8
 
7
- Hash[keys.zip(values)]
9
+ keys.zip(values).to_h
8
10
  end
9
11
  end # DList
10
12
  end # Watir
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  #
3
5
  # Base class for HTML elements.
@@ -13,6 +15,7 @@ module Watir
13
15
  include JSExecution
14
16
  include Locators::ClassHelpers
15
17
  include Scrolling
18
+ include SearchContext
16
19
 
17
20
  attr_accessor :keyword
18
21
  attr_reader :selector
@@ -54,38 +57,11 @@ module Watir
54
57
  build unless @element
55
58
  end
56
59
 
57
- #
58
- # Returns true if element exists.
59
- # Checking for staleness is deprecated
60
- #
61
- # @return [Boolean]
62
- #
63
-
64
- def exists?
65
- if located? && stale?
66
- reset!
67
- elsif located?
68
- return true
69
- end
70
-
71
- assert_exists
72
- true
73
- rescue UnknownObjectException, UnknownFrameException
74
- false
75
- end
76
- alias exist? exists?
77
-
78
60
  def inspect
79
- string = "#<#{self.class}: "
80
- string << "keyword: #{keyword} " if keyword
81
- string << "located: #{located?}; "
82
- string << if @selector.empty?
83
- '{element: (selenium element)}'
84
- else
85
- selector_string
86
- end
87
- string << '>'
88
- string
61
+ keyword_string = keyword ? "keyword: #{keyword} " : ''
62
+ located = "located: #{located?}; "
63
+ element_string = @selector.empty? ? '{element: (selenium element)}' : selector_string
64
+ "#<#{self.class}: #{keyword_string}#{located}#{element_string}>"
89
65
  end
90
66
 
91
67
  #
@@ -372,7 +348,7 @@ module Watir
372
348
  def attribute_values
373
349
  result = element_call { execute_js(:attributeValues, @element) }
374
350
  result.keys.each do |key|
375
- next unless key == key[/[a-zA-Z\-]*/]
351
+ next unless key == key[/[a-zA-Z-]*/]
376
352
 
377
353
  result[key.tr('-', '_').to_sym] = result.delete(key)
378
354
  end
@@ -505,6 +481,17 @@ module Watir
505
481
  assert_exists
506
482
  @element
507
483
  end
484
+ alias we wd
485
+
486
+ #
487
+ # Returns shadow root of element
488
+ #
489
+ # @return [Watir::ShadowRoot]
490
+ #
491
+
492
+ def shadow_root
493
+ ShadowRoot.new(self)
494
+ end
508
495
 
509
496
  #
510
497
  # Returns true if this element is present and enabled on the page.
@@ -700,18 +687,6 @@ module Watir
700
687
 
701
688
  protected
702
689
 
703
- def wait_for_exists
704
- return if located? # Performance shortcut
705
-
706
- begin
707
- @query_scope.wait_for_exists unless @query_scope.is_a? Browser
708
- wait_until(element_reset: true, &:exists?)
709
- rescue Wait::TimeoutError
710
- msg = "timed out after #{Watir.default_timeout} seconds, waiting for #{inspect} to be located"
711
- raise unknown_exception, msg
712
- end
713
- end
714
-
715
690
  def wait_for_present
716
691
  return true if present?
717
692
 
@@ -758,7 +733,7 @@ module Watir
758
733
  end
759
734
 
760
735
  def ensure_context
761
- if @query_scope.is_a?(Browser) || !@query_scope.located? && @query_scope.is_a?(IFrame)
736
+ if @query_scope.is_a?(Browser) || (!@query_scope.located? && @query_scope.is_a?(IFrame))
762
737
  @query_scope.browser.locate
763
738
  elsif @query_scope.located? && @query_scope.stale?
764
739
  @query_scope.locate
@@ -772,10 +747,6 @@ module Watir
772
747
 
773
748
  private
774
749
 
775
- def unknown_exception
776
- UnknownObjectException
777
- end
778
-
779
750
  def raise_writable
780
751
  message = "element present and enabled, but timed out after #{Watir.default_timeout} seconds, " \
781
752
  "waiting for #{inspect} to not be readonly"
@@ -790,7 +761,7 @@ module Watir
790
761
 
791
762
  def raise_present
792
763
  message = "element located, but timed out after #{Watir.default_timeout} seconds, " \
793
- "waiting for #{inspect} to be present"
764
+ "waiting for #{inspect} to be present"
794
765
  raise unknown_exception, message
795
766
  end
796
767
 
@@ -806,66 +777,19 @@ module Watir
806
777
  raise TypeError, "expected Watir::Element, got #{obj.inspect}:#{obj.class}" unless obj.is_a? Element
807
778
  end
808
779
 
809
- # TODO: - this will get addressed with Watir::Executor implementation
810
- # rubocop:disable Metrics/AbcSize
811
- # rubocop:disable Metrics/MethodLength
812
- # rubocop:disable Metrics/CyclomaticComplexity:
813
- # rubocop:disable Metrics/PerceivedComplexity::
814
- def element_call(precondition = nil, &block)
815
- caller = caller_locations(1, 1)[0].label
816
- already_locked = browser.timer.locked?
817
- browser.timer = Wait::Timer.new(timeout: Watir.default_timeout) unless already_locked
818
-
819
- begin
820
- check_condition(precondition, caller)
821
- Watir.logger.debug "-> `Executing #{inspect}##{caller}`"
822
- yield
823
- rescue unknown_exception => e
824
- element_call(:wait_for_exists, &block) if precondition.nil?
825
- msg = e.message
826
- msg += '; Maybe look in an iframe?' if @query_scope.iframe.exists?
827
- custom_attributes = !defined?(@locator) || @locator.nil? ? [] : selector_builder.custom_attributes
828
- unless custom_attributes.empty?
829
- msg += "; Watir treated #{custom_attributes} as a non-HTML compliant attribute, ensure that was intended"
830
- end
831
- raise unknown_exception, msg
832
- rescue Selenium::WebDriver::Error::StaleElementReferenceError, Selenium::WebDriver::Error::NoSuchElementError
833
- reset!
834
- retry
835
- # TODO: - InvalidElementStateError is deprecated, so no longer calling `raise_disabled`
836
- # need a better way to handle this
837
- rescue Selenium::WebDriver::Error::ElementNotInteractableError
838
- raise_present unless browser.timer.remaining_time.positive?
839
- raise_present unless %i[wait_for_present wait_for_enabled wait_for_writable].include?(precondition)
840
- retry
841
- rescue Selenium::WebDriver::Error::NoSuchWindowError
842
- raise NoMatchingWindowFoundException, 'browser window was closed'
843
- ensure
844
- Watir.logger.debug "<- `Completed #{inspect}##{caller}`"
845
- browser.timer.reset! unless already_locked
846
- end
847
- end
848
- # rubocop:enable Metrics/AbcSize
849
- # rubocop:enable Metrics/MethodLength
850
- # rubocop:enable Metrics/CyclomaticComplexity
851
- # rubocop:enable Metrics/PerceivedComplexity
852
-
853
- def check_condition(condition, caller)
854
- Watir.logger.debug "<- `Verifying precondition #{inspect}##{condition} for #{caller}`"
855
- begin
856
- condition.nil? ? assert_exists : send(condition)
857
- Watir.logger.debug "<- `Verified precondition #{inspect}##{condition || 'assert_exists'}`"
858
- rescue unknown_exception
859
- raise unless condition.nil?
860
-
861
- Watir.logger.debug "<- `Unable to satisfy precondition #{inspect}##{condition}`"
862
- check_condition(:wait_for_exists, caller)
780
+ def custom_message
781
+ msg = ''
782
+ msg += '; Maybe look in an iframe?' if @query_scope.iframe.exists?
783
+ custom_attributes = !defined?(@locator) || @locator.nil? ? [] : selector_builder.custom_attributes
784
+ unless custom_attributes.empty?
785
+ msg += "; Watir treated #{custom_attributes} as a non-HTML compliant attribute, ensure that was intended"
863
786
  end
787
+ msg
864
788
  end
865
789
 
866
790
  def method_missing(meth, *args, &blk)
867
791
  method = meth.to_s
868
- if method =~ Locators::Element::SelectorBuilder::WILDCARD_ATTRIBUTE
792
+ if Locators::Element::SelectorBuilder::WILDCARD_ATTRIBUTE.match?(method)
869
793
  attribute_value(meth, *args)
870
794
  elsif UserEditable.instance_methods(false).include?(meth) && content_editable?
871
795
  @content_editable = true
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  class FileField < Input
3
5
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  class Font < HTMLElement
3
5
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  class Form < HTMLElement
3
5
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  class Hidden < Input
3
5
  def visible?
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  class IFrame < HTMLElement
3
5
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  class Image < HTMLElement
3
5
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  class Input < HTMLElement
3
5
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  module Container
3
5
  alias link a
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  module List
3
5
  include Enumerable
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  #
3
5
  # Represents an option in a select list.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  class Radio < Input
3
5
  def build
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  #
3
5
  # Custom class representing table row (tr).
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  class Select < HTMLElement
3
5
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  class Table < HTMLElement
3
5
  include RowContainer
@@ -31,7 +33,7 @@ module Watir
31
33
 
32
34
  all_rows.entries[1..].map do |row|
33
35
  cell_size_check(header_row, row)
34
- Hash[headers(header_row).map(&:text).zip(row.cells.map(&:text))]
36
+ headers(header_row).map(&:text).zip(row.cells.map(&:text)).to_h
35
37
  end
36
38
  end
37
39
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  class TableCell < HTMLElement
3
5
  def column_header
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Watir
2
4
  class TableRow < HTMLElement
3
5
  include CellContainer