watir 7.0.0.beta1 → 7.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/tests.yml +7 -3
  3. data/.rubocop.yml +2 -7
  4. data/CHANGES.md +16 -0
  5. data/lib/watir/browser.rb +18 -4
  6. data/lib/watir/capabilities.rb +1 -1
  7. data/lib/watir/elements/element.rb +32 -3
  8. data/lib/watir/elements/font.rb +1 -0
  9. data/lib/watir/elements/iframe.rb +0 -1
  10. data/lib/watir/elements/radio.rb +2 -2
  11. data/lib/watir/elements/select.rb +63 -40
  12. data/lib/watir/has_window.rb +2 -0
  13. data/lib/watir/locators.rb +4 -0
  14. data/lib/watir/locators/element/matcher.rb +1 -1
  15. data/lib/watir/locators/element/selector_builder.rb +0 -3
  16. data/lib/watir/locators/option/matcher.rb +24 -0
  17. data/lib/watir/locators/option/selector_builder.rb +8 -0
  18. data/lib/watir/locators/option/selector_builder/xpath.rb +37 -0
  19. data/lib/watir/logger.rb +3 -74
  20. data/lib/watir/radio_set.rb +1 -0
  21. data/lib/watir/screenshot.rb +2 -8
  22. data/lib/watir/user_editable.rb +10 -3
  23. data/lib/watir/version.rb +1 -1
  24. data/lib/watir/window.rb +15 -4
  25. data/lib/watir/window_collection.rb +9 -0
  26. data/lib/watirspec.rb +4 -2
  27. data/lib/watirspec/guards.rb +1 -1
  28. data/lib/watirspec/server.rb +1 -1
  29. data/spec/spec_helper.rb +0 -10
  30. data/spec/unit/capabilities_spec.rb +1 -1
  31. data/spec/unit/match_elements/element_spec.rb +11 -0
  32. data/spec/watirspec/after_hooks_spec.rb +22 -45
  33. data/spec/watirspec/browser_spec.rb +185 -206
  34. data/spec/watirspec/cookies_spec.rb +47 -52
  35. data/spec/watirspec/drag_and_drop_spec.rb +5 -7
  36. data/spec/watirspec/elements/area_spec.rb +1 -5
  37. data/spec/watirspec/elements/button_spec.rb +4 -8
  38. data/spec/watirspec/elements/checkbox_spec.rb +2 -4
  39. data/spec/watirspec/elements/date_field_spec.rb +13 -16
  40. data/spec/watirspec/elements/date_time_field_spec.rb +14 -13
  41. data/spec/watirspec/elements/dd_spec.rb +3 -4
  42. data/spec/watirspec/elements/del_spec.rb +10 -12
  43. data/spec/watirspec/elements/div_spec.rb +41 -50
  44. data/spec/watirspec/elements/dl_spec.rb +4 -12
  45. data/spec/watirspec/elements/element_spec.rb +155 -89
  46. data/spec/watirspec/elements/elements_spec.rb +8 -9
  47. data/spec/watirspec/elements/filefield_spec.rb +5 -7
  48. data/spec/watirspec/elements/form_spec.rb +1 -1
  49. data/spec/watirspec/elements/forms_spec.rb +3 -5
  50. data/spec/watirspec/elements/frame_spec.rb +17 -22
  51. data/spec/watirspec/elements/iframe_spec.rb +21 -27
  52. data/spec/watirspec/elements/ins_spec.rb +10 -12
  53. data/spec/watirspec/elements/link_spec.rb +24 -26
  54. data/spec/watirspec/elements/links_spec.rb +8 -9
  55. data/spec/watirspec/elements/radio_spec.rb +11 -14
  56. data/spec/watirspec/elements/select_list_spec.rb +248 -117
  57. data/spec/watirspec/elements/span_spec.rb +10 -12
  58. data/spec/watirspec/elements/table_nesting_spec.rb +31 -34
  59. data/spec/watirspec/elements/table_spec.rb +11 -13
  60. data/spec/watirspec/elements/tbody_spec.rb +10 -12
  61. data/spec/watirspec/elements/td_spec.rb +4 -6
  62. data/spec/watirspec/elements/text_field_spec.rb +10 -12
  63. data/spec/watirspec/elements/tr_spec.rb +5 -7
  64. data/spec/watirspec/user_editable_spec.rb +26 -28
  65. data/spec/watirspec/wait_spec.rb +255 -258
  66. data/spec/watirspec/window_switching_spec.rb +199 -200
  67. data/spec/watirspec_helper.rb +34 -31
  68. metadata +5 -6
  69. data/spec/implementation_spec.rb +0 -24
  70. data/spec/unit/logger_spec.rb +0 -81
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 994057876b1f2ad97972dfe3a43f139cf761aefabcacbc7ab1d2bc659693e70e
4
- data.tar.gz: d0bcac18f0cfe6d9d2a0258bb57a8e2e9f0804feda5eeaf8e5d840fd859de3c4
3
+ metadata.gz: 58e502e12250523b603418f4d352663d893762126b5cdb48e046e58a8a19bc20
4
+ data.tar.gz: cd3405850e28a040968c7df99f93d87a2593ff7b2c34da4b8f0789cbe023df42
5
5
  SHA512:
6
- metadata.gz: b652878ab0a6b4365e3226a664945a86c96f8f94c6b0cd4744c0809a66d8fb49ff37951726699df217856d413d4104778164218ca365a1481ec6ab1212c2f12e
7
- data.tar.gz: f6f5f093a6072f1456055853d2b93ce994b978897668eb3348ee6fe70fa872eea681c97481d254830f5d028cce0029bb9e45a5dd25d50d785a6e144592d429a2
6
+ metadata.gz: 0557ff99666998a7104b51ae07439d7245e645e2fe543d8a50ed2c191f3c8d5a04423e52c22b8065e3a5f7793ee7ce9101d64f54c0ca4349eec23d1b5057c3e7
7
+ data.tar.gz: 1d36608975ae96d51ce119cd55f96a2d28eb0c9de2cb1194e33d9fe1429c09c5fdd86022c07bf1350617e60c6f667ce0bd818428f545e37f8eaebf65361fb883
@@ -61,13 +61,17 @@ jobs:
61
61
  task: [ chrome, firefox, edge ]
62
62
  include:
63
63
  - os: 'macos-latest'
64
- task: 'safari'
65
64
  ruby: 2.6
65
+ task: 'safari'
66
66
  - os: 'macos-latest'
67
+ ruby: 3.0
67
68
  task: 'safari'
69
+ - os: 'windows-latest'
70
+ ruby: 2.6
71
+ task: 'ie'
72
+ - os: 'windows-latest'
68
73
  ruby: 3.0
69
- # - os: 'windows-latest'
70
- # task: 'ie'
74
+ task: 'ie'
71
75
  exclude:
72
76
  - os: 'ubuntu-latest'
73
77
  task: 'edge'
data/.rubocop.yml CHANGED
@@ -19,17 +19,12 @@ Lint/UnifiedInteger:
19
19
 
20
20
  # Default: 17
21
21
  Metrics/AbcSize:
22
- Max: 20
22
+ Max: 22
23
23
  Exclude:
24
- - 'lib/watir/capabilities.rb'
25
24
  - 'lib/watir/locators/element/selector_builder.rb'
26
- - 'lib/watir/locators/element/selector_builder/regexp_disassembler.rb'
27
- - 'lib/watir/locators/element/selector_builder/xpath.rb'
28
- - 'lib/watir/locators/element/locator.rb'
25
+ - 'lib/watir/locators/element/selector_builder/*.rb'
29
26
  - 'lib/watir/generator/base/generator.rb'
30
- - 'lib/watir/generator/base/visitor.rb'
31
27
  - 'spec/locator_spec_helper.rb'
32
- - 'spec/watirspec_helper.rb'
33
28
 
34
29
  Metrics/BlockLength:
35
30
  Exclude:
data/CHANGES.md CHANGED
@@ -1,3 +1,19 @@
1
+ ### 7.0.0.beta2 (2021-03-28)
2
+
3
+ * Replace Watir Logger implementation with Selenium Logger subclass
4
+ * Change Watir Guards to use Selenium's new Guards. Tests run as pending when guarded.
5
+ * Implement `#set` as standard interface for each Input Element (#405)
6
+ * Implement `Element#set` to take correct `#set` behavior based on evaluated element (#664)
7
+ * Optimize Performance for Select Lists (#846)
8
+ * Allow user to set values on Select List exclusively by `:label`, `:text`, or `:value` (#846)
9
+ * Allow user to check if option selected in Select List by `:label`, `:text`, or `:value` (#929)
10
+ * Implement `Window#restore!` to return to original Window and close all others (#923)
11
+ * Minor performance improvement for iterating over windows (#923)
12
+ * Implement `Browser#closed?`; same as `Browser#exists?` without the Windows checks (#923)
13
+ * Update methods that use Selenium's Actions class to scroll element into view before acting (#847)
14
+ * Fix bug for `:text` locator with `Regexp` value based on whitespace (#924)
15
+ * Remove executing after hooks when changing frames (#888)
16
+
1
17
  ### 7.0.0.beta1 (2021-03-18)
2
18
 
3
19
  * Requires Selenium 4
data/lib/watir/browser.rb CHANGED
@@ -55,16 +55,21 @@ module Watir
55
55
  @default_context = true
56
56
  end
57
57
 
58
+ # rubocop:disable Metrics/AbcSize
59
+ # TODO: w3c default behavior does not like checking if alert exists
58
60
  def inspect
59
61
  if alert.exists?
60
62
  format('#<%s:0x%x alert=true>', self.class, hash * 2)
61
63
  else
62
64
  format('#<%s:0x%x url=%s title=%s>', self.class, hash * 2, url.inspect, title.inspect)
63
65
  end
66
+ rescue Selenium::WebDriver::Error::NoSuchWindowError
67
+ format('#<%s:0x%x closed=%s>', self.class, hash * 2, closed?)
64
68
  rescue Errno::ECONNREFUSED
65
69
  format('#<%s:0x%x closed=true>', self.class, hash * 2)
66
70
  end
67
71
  alias selector_string inspect
72
+ # rubocop:enable Metrics/AbcSize
68
73
 
69
74
  #
70
75
  # Returns URL of current page.
@@ -101,13 +106,23 @@ module Watir
101
106
  #
102
107
 
103
108
  def close
104
- return if @closed
109
+ return if closed?
105
110
 
106
111
  @driver.quit
107
112
  @closed = true
108
113
  end
109
114
  alias quit close
110
115
 
116
+ #
117
+ # Returns true if browser is closed and false otherwise.
118
+ #
119
+ # @return [Boolean]
120
+ #
121
+
122
+ def closed?
123
+ @closed
124
+ end
125
+
111
126
  #
112
127
  # Handles cookies.
113
128
  #
@@ -252,12 +267,12 @@ module Watir
252
267
  #
253
268
 
254
269
  def exist?
255
- !@closed && window.present?
270
+ !closed? && window.present?
256
271
  end
257
272
  alias exists? exist?
258
273
 
259
274
  def locate
260
- raise Error, 'browser was closed' if @closed
275
+ raise Error, 'browser was closed' if closed?
261
276
 
262
277
  ensure_context
263
278
  end
@@ -267,7 +282,6 @@ module Watir
267
282
 
268
283
  driver.switch_to.default_content
269
284
  @default_context = true
270
- after_hooks.run
271
285
  end
272
286
 
273
287
  def browser
@@ -55,7 +55,7 @@ module Watir
55
55
  http_client
56
56
  when Selenium::WebDriver::Remote::Http::Common
57
57
  Watir.logger.warn 'Check out the new Watir::HttpClient and let us know if there are missing features you need',
58
- ids: [:watir_client]
58
+ id: [:watir_client]
59
59
  http_client
60
60
  else
61
61
  raise TypeError, ':http_client must be a Hash or a Selenium HTTP Client instance'
@@ -163,6 +163,26 @@ module Watir
163
163
  browser.after_hooks.run
164
164
  end
165
165
 
166
+ #
167
+ # Determines the correct action based on subtype and takes it.
168
+ # Default is to click element
169
+ #
170
+
171
+ def set(*args)
172
+ subtype = to_subtype
173
+ if subtype.is_a?(Radio) && [String, Regexp].include?(args.first.class)
174
+ RadioSet.new(@query_scope, selector).set(*args)
175
+ elsif subtype.class.included_modules.include?(UserEditable) || subtype.public_methods(false).include?(:set)
176
+ subtype.set(*args)
177
+ elsif @content_editable || content_editable?
178
+ @content_editable = true
179
+ extend UserEditable
180
+ set(*args)
181
+ elsif args.empty? || args.first
182
+ click(*args)
183
+ end
184
+ end
185
+
166
186
  #
167
187
  # Simulates JavaScript click event on element.
168
188
  #
@@ -184,7 +204,10 @@ module Watir
184
204
  #
185
205
 
186
206
  def double_click
187
- element_call(:wait_for_present) { driver.action.double_click(@element).perform }
207
+ element_call(:wait_for_present) do
208
+ scroll.to
209
+ driver.action.double_click(@element).perform
210
+ end
188
211
  browser.after_hooks.run
189
212
  end
190
213
 
@@ -219,6 +242,7 @@ module Watir
219
242
 
220
243
  def right_click(*modifiers)
221
244
  element_call(:wait_for_present) do
245
+ scroll.to
222
246
  action = driver.action
223
247
  if modifiers.any?
224
248
  modifiers.each { |mod| action.key_down mod }
@@ -242,7 +266,10 @@ module Watir
242
266
  #
243
267
 
244
268
  def hover
245
- element_call(:wait_for_present) { driver.action.move_to(@element).perform }
269
+ element_call(:wait_for_present) do
270
+ scroll.to
271
+ driver.action.move_to(@element).perform
272
+ end
246
273
  end
247
274
 
248
275
  #
@@ -259,6 +286,7 @@ module Watir
259
286
  assert_is_element other
260
287
 
261
288
  value = element_call(:wait_for_present) do
289
+ scroll.to
262
290
  driver.action
263
291
  .drag_and_drop(@element, other.wd)
264
292
  .perform
@@ -280,6 +308,7 @@ module Watir
280
308
 
281
309
  def drag_and_drop_by(right_by, down_by)
282
310
  element_call(:wait_for_present) do
311
+ scroll.to
283
312
  driver.action
284
313
  .drag_and_drop_by(@element, right_by, down_by)
285
314
  .perform
@@ -672,7 +701,7 @@ module Watir
672
701
 
673
702
  def wait_for_enabled
674
703
  wait_for_exists
675
- return unless [Input, Button, Select, Option].any? { |c| is_a? c } || content_editable
704
+ return unless [Input, Button, Select, Option].any? { |c| is_a? c } || @content_editable
676
705
  return if enabled?
677
706
 
678
707
  begin
@@ -2,6 +2,7 @@ module Watir
2
2
  class Font < HTMLElement
3
3
  #
4
4
  # size of font
5
+ # This is in the whatwg spec was not generated: https://html.spec.whatwg.org/#htmlfontelement
5
6
  #
6
7
  # @return [Integer]
7
8
  #
@@ -126,7 +126,6 @@ module Watir
126
126
  def switch!
127
127
  @driver.switch_to.frame @element
128
128
  @browser.default_context = false
129
- @browser.after_hooks.run
130
129
  rescue Selenium::WebDriver::Error::NoSuchFrameError => e
131
130
  raise UnknownFrameException, e.message
132
131
  end
@@ -9,8 +9,8 @@ module Watir
9
9
  # Selects this radio button.
10
10
  #
11
11
 
12
- def set
13
- click unless set?
12
+ def set(bool = true)
13
+ click if bool && !set?
14
14
  end
15
15
  alias select set
16
16
 
@@ -5,7 +5,7 @@ module Watir
5
5
  #
6
6
 
7
7
  def clear
8
- raise Exception::Error, 'you can only clear multi-selects' unless multiple?
8
+ raise Exception::Error, 'you can only clear multi-selects' unless multiple_select_list?
9
9
 
10
10
  selected_options.each(&:click)
11
11
  end
@@ -18,7 +18,7 @@ module Watir
18
18
  #
19
19
 
20
20
  def include?(str_or_rx)
21
- option(text: str_or_rx).exist? || option(label: str_or_rx).exist?
21
+ option(text: str_or_rx).exist? || option(label: str_or_rx).exist? || option(value: str_or_rx).exist?
22
22
  end
23
23
 
24
24
  #
@@ -29,14 +29,16 @@ module Watir
29
29
  # @return [String] The text of the option selected. If multiple options match, returns the first match.
30
30
  #
31
31
 
32
- def select(*str_or_rx)
33
- if str_or_rx.size > 1 || str_or_rx.first.is_a?(Array)
34
- str_or_rx.flatten.map { |v| select_all_by v }.first
32
+ def select(*str_or_rx, text: nil, value: nil, label: nil)
33
+ key, value = parse_select_args(str_or_rx, text, value, label)
34
+
35
+ if value.size > 1 || value.first.is_a?(Array)
36
+ value.flatten.map { |v| select_all_by key, v }.first
35
37
  else
36
- found = find_options(:value, str_or_rx.flatten.first).first
37
- select_matching([found])
38
+ select_matching([find_option(key, value.flatten.first)])
38
39
  end
39
40
  end
41
+ alias set select
40
42
 
41
43
  #
42
44
  # Uses JavaScript to select the option whose text matches the given string.
@@ -45,11 +47,13 @@ module Watir
45
47
  # @raise [Watir::Exception::NoValueFoundException] if the value does not exist.
46
48
  #
47
49
 
48
- def select!(*str_or_rx)
49
- if str_or_rx.size > 1 || str_or_rx.first.is_a?(Array)
50
- str_or_rx.flatten.map { |v| select_by! v, :multiple }.first
50
+ def select!(*str_or_rx, text: nil, value: nil, label: nil)
51
+ key, value = parse_select_args(str_or_rx, text, value, label)
52
+
53
+ if value.size > 1 || value.first.is_a?(Array)
54
+ value.flatten.map { |v| select_by! key, v, :multiple }.first
51
55
  else
52
- str_or_rx.flatten.map { |v| select_by! v, :single }.first
56
+ value.flatten.map { |v| select_by! key, v, :single }.first
53
57
  end
54
58
  end
55
59
 
@@ -61,16 +65,10 @@ module Watir
61
65
  # @return [Boolean]
62
66
  #
63
67
 
64
- def selected?(str_or_rx)
65
- by_text = options(text: str_or_rx)
66
- return true if by_text.find(&:selected?)
67
-
68
- by_label = options(label: str_or_rx)
69
- return true if by_label.find(&:selected?)
68
+ def selected?(*str_or_rx, text: nil, value: nil, label: nil)
69
+ key, value = parse_select_args(str_or_rx, text, value, label)
70
70
 
71
- return false unless (by_text.size + by_label.size).zero?
72
-
73
- raise(UnknownObjectException, "Unable to locate option matching #{str_or_rx.inspect}")
71
+ find_option(key, value.first).selected?
74
72
  end
75
73
 
76
74
  #
@@ -81,8 +79,7 @@ module Watir
81
79
  #
82
80
 
83
81
  def value
84
- option = selected_options.first
85
- option&.value
82
+ selected_options.first&.value
86
83
  end
87
84
 
88
85
  #
@@ -92,9 +89,9 @@ module Watir
92
89
  # @return [String, nil]
93
90
  #
94
91
 
92
+ # TODO: What is default behavior without #first ?
95
93
  def text
96
- option = selected_options.first
97
- option&.text
94
+ selected_options.first&.text
98
95
  end
99
96
 
100
97
  # Returns an array of currently selected options.
@@ -108,10 +105,29 @@ module Watir
108
105
 
109
106
  private
110
107
 
111
- def select_by!(str_or_rx, number)
108
+ def multiple_select_list?
109
+ @multiple_select = @multiple_select.nil? ? multiple? : @multiple_select
110
+ end
111
+
112
+ def parse_select_args(str_or_rx, text, value, label)
113
+ selectors = {}
114
+ selectors[:any] = str_or_rx unless str_or_rx.empty?
115
+ selectors[:text] = Array[text] if text
116
+ selectors[:value] = Array[value] if value
117
+ selectors[:label] = Array[label] if label
118
+
119
+ raise ArgumentError, "too many arguments used for Select#select: #{selectors}" if selectors.size > 1
120
+
121
+ selectors.first
122
+ end
123
+
124
+ def select_by!(key, str_or_rx, number)
125
+ str_or_rx = type_check(str_or_rx)
126
+
112
127
  js_rx = process_str_or_rx(str_or_rx)
128
+ approaches = key == :any ? %w[Text Label Value] : [key.to_s.capitalize]
113
129
 
114
- %w[Text Label Value].each do |approach|
130
+ approaches.each do |approach|
115
131
  element_call { execute_js("selectOptions#{approach}", self, js_rx, number.to_s) }
116
132
  return selected_options.first.text if matching_option?(approach.downcase, str_or_rx)
117
133
  end
@@ -147,34 +163,41 @@ module Watir
147
163
  false
148
164
  end
149
165
 
150
- def select_all_by(str_or_rx)
151
- raise Error, 'you can only use #select_all on multi-selects' unless multiple?
166
+ def select_all_by(key, str_or_rx)
167
+ raise Error, 'you can only use #select_all on multi-selects' unless multiple_select_list?
152
168
 
153
- select_matching(find_options(:text, str_or_rx))
169
+ select_matching(find_options(key, str_or_rx))
154
170
  end
155
171
 
156
- def find_options(how, str_or_rx)
157
- msg = "expected String, Numeric or Regexp, got #{str_or_rx.inspect}:#{str_or_rx.class}"
158
- raise TypeError, msg unless [String, Numeric, Regexp].any? { |k| str_or_rx.is_a?(k) }
172
+ def find_option(key, str_or_rx)
173
+ val = type_check(str_or_rx)
159
174
 
160
- wait_while do
161
- @found = how == :value ? options(value: str_or_rx) : []
162
- @found = options(text: str_or_rx) if @found.empty?
163
- @found = options(label: str_or_rx) if @found.empty?
164
- @found.empty?
165
- end
166
- @found
175
+ option(key => val).wait_until(&:exists?)
176
+ rescue Wait::TimeoutError
177
+ raise_no_value_found(str_or_rx)
178
+ end
179
+
180
+ def find_options(key, str_or_rx)
181
+ val = type_check(str_or_rx)
182
+
183
+ options(key => val).wait_until(&:exists?)
167
184
  rescue Wait::TimeoutError
168
185
  raise_no_value_found(str_or_rx)
169
186
  end
170
187
 
188
+ def type_check(str_or_rx)
189
+ str_or_rx = str_or_rx.to_s if str_or_rx.is_a?(Numeric)
190
+ return str_or_rx if [String, Regexp].any? { |k| str_or_rx.is_a?(k) }
191
+
192
+ raise TypeError, "expected String, Numeric or Regexp, got #{str_or_rx.inspect}:#{str_or_rx.class}"
193
+ end
194
+
171
195
  # TODO: Consider locating the Select List before throwing the exception
172
196
  def raise_no_value_found(str_or_rx)
173
197
  raise NoValueFoundException, "#{str_or_rx.inspect} not found in #{inspect}"
174
198
  end
175
199
 
176
200
  def select_matching(elements)
177
- elements = [elements.first] unless multiple?
178
201
  elements.each { |e| e.click unless e.selected? }
179
202
  elements.first.exists? ? elements.first.text : ''
180
203
  end