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
@@ -1,5 +1,7 @@
1
1
  module Watir
2
2
  module HasWindow
3
+ attr_writer :original_window
4
+
3
5
  #
4
6
  # Returns browser windows array.
5
7
  #
@@ -14,6 +14,10 @@ require 'watir/locators/button/matcher'
14
14
  require 'watir/locators/cell/selector_builder'
15
15
  require 'watir/locators/cell/selector_builder/xpath'
16
16
 
17
+ require 'watir/locators/option/matcher'
18
+ require 'watir/locators/option/selector_builder'
19
+ require 'watir/locators/option/selector_builder/xpath'
20
+
17
21
  require 'watir/locators/row/selector_builder'
18
22
  require 'watir/locators/row/selector_builder/xpath'
19
23
 
@@ -82,7 +82,7 @@ module Watir
82
82
  when :tag_name
83
83
  element.tag_name.downcase
84
84
  when :text
85
- execute_js(:getTextContent, element)
85
+ execute_js(:getTextContent, element).gsub(/\s+/, ' ').strip
86
86
  when :visible
87
87
  element.displayed?
88
88
  when :visible_text
@@ -88,9 +88,6 @@ module Watir
88
88
 
89
89
  def normalize_locator(how, what)
90
90
  case how
91
- when 'text'
92
- Watir.logger.deprecate "String 'text' as a locator", 'Symbol :text', ids: [:text_string]
93
- [:text, what]
94
91
  when :tag_name
95
92
  what = what.to_s if what.is_a?(::Symbol)
96
93
  [how, what]
@@ -0,0 +1,24 @@
1
+ module Watir
2
+ module Locators
3
+ class Option
4
+ class Matcher < Element::Matcher
5
+ def fetch_value(element, how)
6
+ case how
7
+ when :any
8
+ [element.attribute(:value),
9
+ execute_js(:getTextContent, element).gsub(/\s+/, ' ').strip,
10
+ element.attribute(:label)]
11
+ else
12
+ super
13
+ end
14
+ end
15
+
16
+ def matches_values?(found, expected)
17
+ return super unless found.is_a?(Array)
18
+
19
+ found.find { |possible| matches_values?(possible, expected) }
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,8 @@
1
+ module Watir
2
+ module Locators
3
+ class Option
4
+ class SelectorBuilder < Element::SelectorBuilder
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,37 @@
1
+ module Watir
2
+ module Locators
3
+ class Option
4
+ class SelectorBuilder
5
+ class XPath < Element::SelectorBuilder::XPath
6
+ private
7
+
8
+ def attribute_string
9
+ result = if @selector.key?(:any)
10
+ to_match = @selector.delete :any
11
+ value = process_attribute(:value, to_match)
12
+ text = process_attribute(:text, to_match)
13
+ label = process_attribute(:label, to_match)
14
+ "[#{value} or #{text} or #{label}]"
15
+ else
16
+ ''
17
+ end
18
+
19
+ attributes = @selector.keys.map { |key|
20
+ process_attribute(key, @selector.delete(key))
21
+ }.flatten.compact
22
+ attribute_values = attributes.empty? ? '' : "[#{attributes.join(' and ')}]"
23
+ "#{result}#{attribute_values}"
24
+ end
25
+
26
+ def add_to_matching(key, regexp, results = nil)
27
+ return unless results.nil? || requires_matching?(results, regexp)
28
+
29
+ return super unless %i[value text label].include? key
30
+
31
+ @built[:any] = regexp
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
data/lib/watir/logger.rb CHANGED
@@ -16,84 +16,13 @@ module Watir
16
16
  # Watir.logger.info('This is info message')
17
17
  # Watir.logger.warn('This is warning message')
18
18
  #
19
- class Logger
20
- extend Forwardable
21
- include ::Logger::Severity
22
-
23
- def_delegators :@logger, :debug, :debug?,
24
- :info, :info?,
25
- :warn?,
26
- :error, :error?,
27
- :fatal, :fatal?,
28
- :level, :level=
29
-
30
- def initialize(progname = 'Watir')
31
- @logger = create_logger($stdout)
32
- @logger.progname = progname
33
- @ignored = []
34
- end
35
-
36
- def ignore(ids)
37
- @ignored.concat Array(ids).map(&:to_s)
38
- end
39
-
40
- def output=(io)
41
- @logger.reopen(io)
42
- end
43
-
44
- #
45
- # Only log a warn message if it is not set to be ignored.
46
- #
47
- def warn(message, ids: [], &block)
48
- msg = ids.empty? ? '' : "[#{ids.map!(&:to_s).map(&:inspect).join(', ')}] "
49
- msg += message
50
- @logger.warn(msg, &block) unless (@ignored & ids).any?
51
- end
52
-
53
- #
54
- # Returns IO object used by logger internally.
55
- #
56
- # Normally, we would have never needed it, but we want to
57
- # use it as IO object for all child processes to ensure their
58
- # output is redirected there.
59
- #
60
- # It is only used in debug level, in other cases output is suppressed.
61
- #
62
- # @api private
63
- #
64
- def io
65
- @logger.instance_variable_get(:@logdev).instance_variable_get(:@dev)
66
- end
67
-
68
- #
69
- # Marks code as deprecated with replacement.
70
- #
71
- # @param [String] old
72
- # @param [String] new
73
- #
74
- def deprecate(old, new, reference: '', ids: [])
75
- return if @ignored.include?('deprecations') || (@ignored & ids.map!(&:to_s)).any?
76
-
77
- msg = ids.empty? ? '' : "[#{ids.map(&:inspect).join(', ')}] "
78
- ref_msg = reference.empty? ? '.' : "; see explanation for this deprecation: #{reference}."
79
- warn "[DEPRECATION] #{msg}#{old} is deprecated. Use #{new} instead#{ref_msg}"
19
+ class Logger < Selenium::WebDriver::Logger
20
+ def initialize
21
+ super('Watir')
80
22
  end
81
23
 
82
24
  def selenium=(val)
83
25
  Selenium::WebDriver.logger.level = val
84
26
  end
85
-
86
- private
87
-
88
- def create_logger(output)
89
- logger = ::Logger.new(output)
90
- logger.progname = 'Watir'
91
- logger.level = ($DEBUG ? DEBUG : WARN)
92
- logger.formatter = proc do |severity, time, progname, msg|
93
- "#{time.strftime('%F %T')} #{severity} #{progname} #{msg}\n"
94
- end
95
-
96
- logger
97
- end
98
27
  end
99
28
  end
@@ -139,6 +139,7 @@ module Watir
139
139
  end
140
140
  raise UnknownObjectException, "Unable to locate radio matching #{str_or_rx.inspect}"
141
141
  end
142
+ alias set select
142
143
 
143
144
  #
144
145
  # Returns true if any of the radio button label matches the given value.
@@ -1,14 +1,8 @@
1
1
  module Watir
2
2
  class Screenshot
3
3
  def initialize(browser)
4
- if browser.is_a? Selenium::WebDriver::Driver
5
- msg = 'Initializing `Watir::Screenshot` with a `Selenium::Driver` instance', 'a `Watir::Browser` instance'
6
- Watir.logger.deprecate msg, ids: [:screenshot_driver]
7
- @driver = browser
8
- else
9
- @browser = browser
10
- @driver = browser.wd
11
- end
4
+ @browser = browser
5
+ @driver = browser.wd
12
6
  end
13
7
 
14
8
  #
@@ -15,16 +15,23 @@ module Watir
15
15
  alias value= set
16
16
 
17
17
  #
18
- # Uses JavaScript to enter most of the given value.
19
- # Selenium is used to enter the first and last characters
18
+ # Returns true if element is user_editable because it has a content_editable attribute set
20
19
  #
21
- # @param [String, Symbol] args
20
+ # @return [Boolean]
22
21
  #
23
22
 
24
23
  def content_editable
25
24
  defined?(@content_editable) && content_editable?
26
25
  end
27
26
 
27
+ #
28
+ # Uses JavaScript to enter most of the given value.
29
+ # Selenium is used to enter the first and last characters
30
+ # This might provide a performance improvement when entering a lot of text on a local machine
31
+ #
32
+ # @param [String, Symbol] args
33
+ #
34
+
28
35
  def set!(*args)
29
36
  msg = '#set! does not support special keys, use #set instead'
30
37
  raise ArgumentError, msg if args.any? { |v| v.is_a?(::Symbol) }
data/lib/watir/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Watir
2
- VERSION = '7.0.0.beta1'.freeze
2
+ VERSION = '7.0.0.beta2'.freeze
3
3
  end
data/lib/watir/window.rb CHANGED
@@ -179,10 +179,21 @@ module Watir
179
179
  # end
180
180
  #
181
181
 
182
- def use(&blk)
183
- @browser.original_window ||= current_window
182
+ def use
184
183
  wait_for_exists
185
- @driver.switch_to.window(handle, &blk)
184
+ cache_current = current_window
185
+ @browser.original_window ||= cache_current
186
+ restore_to = unless cache_current == handle
187
+ @driver.switch_to.window(handle)
188
+ cache_current
189
+ end
190
+ if block_given?
191
+ begin
192
+ yield
193
+ ensure
194
+ @driver.switch_to.window(restore_to) if restore_to
195
+ end
196
+ end
186
197
  self
187
198
  end
188
199
 
@@ -205,7 +216,7 @@ module Watir
205
216
  end
206
217
 
207
218
  def assert_exists
208
- return if @driver.window_handles.include?(handle)
219
+ return if !handle.nil? && @driver.window_handles.include?(handle)
209
220
 
210
221
  raise(NoMatchingWindowFoundException, selector_string)
211
222
  end
@@ -39,6 +39,15 @@ module Watir
39
39
  end
40
40
  alias eql? ==
41
41
 
42
+ def restore!
43
+ return if @browser.closed?
44
+
45
+ window_list.reject { |win| win.handle == @browser.original_window.handle }.each(&:close)
46
+ @browser.original_window.use
47
+ rescue StandardError
48
+ @browser.close
49
+ end
50
+
42
51
  def reset!
43
52
  @window_list = nil
44
53
  end
data/lib/watirspec.rb CHANGED
@@ -53,7 +53,9 @@ module WatirSpec
53
53
  @implementation = imp
54
54
  end
55
55
 
56
- def new_browser
56
+ def new_browser(pause = 1)
57
+ sleep pause
58
+
57
59
  klass = WatirSpec.implementation.browser_class
58
60
  args = Array(WatirSpec.implementation.browser_args).map { |e| e.is_a?(Hash) ? e.dup : e }
59
61
 
@@ -80,7 +82,7 @@ module WatirSpec
80
82
  info << @implementation.driver_info
81
83
 
82
84
  Watir.logger.warn "running watirspec against #{info.join ' '} using:\n#{WatirSpec.implementation.inspect_args}",
83
- ids: [:browser_info]
85
+ id: [:browser_info]
84
86
  rescue StandardError
85
87
  # ignored
86
88
  end
@@ -21,7 +21,7 @@ module WatirSpec
21
21
  guard_name = "#{guard[:name]}:".ljust(15)
22
22
  str << " \t#{guard_name} #{guard[:data].inspect}\n"
23
23
  end
24
- Watir.logger.warn str, ids: [:guard_names]
24
+ Watir.logger.warn str, id: [:guard_names]
25
25
  end
26
26
  end
27
27
  end # class << self
@@ -56,7 +56,7 @@ module WatirSpec
56
56
 
57
57
  client.write(response(status, headers, body))
58
58
  rescue Errno::ECONNRESET
59
- Watir.logger.warn 'Client reset connection, skipping.', ids: [:reset_connection]
59
+ Watir.logger.warn 'Client reset connection, skipping.', id: [:reset_connection]
60
60
  ensure
61
61
  client.close
62
62
  end
data/spec/spec_helper.rb CHANGED
@@ -20,13 +20,3 @@ end
20
20
  require 'watir'
21
21
  require 'locator_spec_helper'
22
22
  require 'rspec'
23
-
24
- if ENV['AUTOMATIC_RETRY']
25
- require 'rspec/retry'
26
- RSpec.configure do |config|
27
- config.verbose_retry = true
28
- config.display_try_failure_messages = true
29
- config.default_retry_count = 3
30
- config.exceptions_to_retry = [IOError, Net::ReadTimeout]
31
- end
32
- end
@@ -1,4 +1,4 @@
1
- require 'watirspec_helper'
1
+ require_relative 'unit_helper'
2
2
 
3
3
  describe Watir::Capabilities do
4
4
  before(:all) { Watir.logger.ignore(:watir_client) }
@@ -332,6 +332,17 @@ describe Watir::Locators::Element::Matcher do
332
332
  end
333
333
 
334
334
  context 'when matching Regular Expressions' do
335
+ it 'with white space' do
336
+ allow(browser).to receive(:execute_script).and_return("\n match this \n", 'no', 'match this')
337
+
338
+ elements = [wd_element,
339
+ wd_element,
340
+ wd_element]
341
+ @values_to_match = {text: /^match this$/}
342
+
343
+ expect(matcher.match(elements, values_to_match, @filter)).to eq [elements[0], elements[2]]
344
+ end
345
+
335
346
  it 'with tag_name' do
336
347
  elements = [wd_element(tag_name: 'div'),
337
348
  wd_element(tag_name: 'span'),
@@ -85,14 +85,12 @@ describe 'Browser::AfterHooks' do
85
85
  expect(@yield).to be true
86
86
  end
87
87
 
88
- bug 'https://gist.github.com/titusfortner/bd32f27ec2458b3a733d83374d156940', :safari do
89
- it 'runs after_hooks after Element#submit' do
90
- browser.goto(WatirSpec.url_for('forms_with_input_elements.html'))
91
- @page_after_hook = proc { @yield = browser.div(id: 'messages').text == 'submit' }
92
- browser.after_hooks.add @page_after_hook
93
- browser.form(id: 'new_user').submit
94
- expect(@yield).to be true
95
- end
88
+ it 'runs after_hooks after Element#submit' do
89
+ browser.goto(WatirSpec.url_for('forms_with_input_elements.html'))
90
+ @page_after_hook = proc { @yield = browser.div(id: 'messages').text == 'submit' }
91
+ browser.after_hooks.add @page_after_hook
92
+ browser.form(id: 'new_user').submit
93
+ expect(@yield).to be true
96
94
  end
97
95
 
98
96
  it 'runs after_hooks after Element#double_click' do
@@ -115,28 +113,6 @@ describe 'Browser::AfterHooks' do
115
113
  expect(@yield).to be true
116
114
  end
117
115
 
118
- it 'runs after_hooks after FramedDriver#switch!' do
119
- browser.goto(WatirSpec.url_for('iframes.html'))
120
- @page_after_hook = proc { @yield = browser.title == 'Iframes' }
121
- browser.after_hooks.add @page_after_hook
122
-
123
- browser.iframe.element(css: '#senderElement').exists?
124
-
125
- expect(@yield).to be true
126
- end
127
-
128
- it 'runs after_hooks after Browser#ensure_context if not @default_context' do
129
- browser.goto(WatirSpec.url_for('iframes.html'))
130
- browser.iframe.element(css: '#senderElement').locate
131
-
132
- @page_after_hook = proc { @yield = browser.title == 'Iframes' }
133
- browser.after_hooks.add @page_after_hook
134
-
135
- browser.locate
136
-
137
- expect(@yield).to be true
138
- end
139
-
140
116
  it 'runs after_hooks after Alert#ok' do
141
117
  browser.goto(WatirSpec.url_for('alerts.html'))
142
118
  @page_after_hook = proc { @yield = browser.title == 'Alerts' }
@@ -187,21 +163,22 @@ describe 'Browser::AfterHooks' do
187
163
  browser.alert.ok
188
164
  end
189
165
 
190
- bug 'Clicking an Element that Closes a Window is returning NoMatchingWindowFoundException', :safari do
191
- bug 'https://github.com/mozilla/geckodriver/issues/1847', :firefox do
192
- it 'does not raise error when running error checks on closed window' do
193
- url = WatirSpec.url_for('window_switching.html')
194
- @page_after_hook = proc { browser.url }
195
- browser.after_hooks.add @page_after_hook
196
- browser.goto url
197
- browser.a(id: 'open').click
198
-
199
- window = browser.window(title: 'closeable window')
200
- window.use
201
- expect { browser.a(id: 'close').click }.to_not raise_error
202
- browser.original_window.use
203
- end
204
- end
166
+ it 'does not raise error when running error checks on closed window',
167
+ except: {browser: :safari,
168
+ reason: 'Clicking an Element that Closes a Window is returning NoMatchingWindowFoundException'},
169
+ exclude: {browser: :firefox,
170
+ platform: :windows,
171
+ reason: 'https://github.com/mozilla/geckodriver/issues/1847'} do
172
+ url = WatirSpec.url_for('window_switching.html')
173
+ @page_after_hook = proc { browser.url }
174
+ browser.after_hooks.add @page_after_hook
175
+ browser.goto url
176
+ browser.a(id: 'open').click
177
+
178
+ window = browser.window(title: 'closeable window')
179
+ window.use
180
+ expect { browser.a(id: 'close').click }.to_not raise_error
181
+ browser.original_window.use
205
182
  end
206
183
  end
207
184