watir 7.1.0 → 7.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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