watir 6.16.2 → 6.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +5 -5
  2. data/.github/actions/enable-safari/action.yml +11 -0
  3. data/.github/actions/install-chrome/action.yml +11 -0
  4. data/.github/workflows/linux.yml +61 -0
  5. data/.github/workflows/mac.yml +55 -0
  6. data/.github/workflows/unit.yml +31 -0
  7. data/.github/workflows/windows.yml +39 -0
  8. data/.rubocop.yml +32 -107
  9. data/.rubocop_todo.yml +36 -0
  10. data/CHANGES.md +42 -0
  11. data/Gemfile +3 -1
  12. data/LICENSE +2 -2
  13. data/README.md +9 -10
  14. data/Rakefile +4 -4
  15. data/lib/watir-webdriver.rb +1 -1
  16. data/lib/watir.rb +1 -1
  17. data/lib/watir/adjacent.rb +8 -10
  18. data/lib/watir/after_hooks.rb +4 -4
  19. data/lib/watir/alert.rb +1 -0
  20. data/lib/watir/attribute_helper.rb +2 -0
  21. data/lib/watir/browser.rb +7 -3
  22. data/lib/watir/capabilities.rb +9 -6
  23. data/lib/watir/cookies.rb +3 -1
  24. data/lib/watir/element_collection.rb +21 -6
  25. data/lib/watir/elements/element.rb +66 -53
  26. data/lib/watir/elements/file_field.rb +1 -0
  27. data/lib/watir/elements/html_elements.rb +0 -1
  28. data/lib/watir/elements/iframe.rb +4 -3
  29. data/lib/watir/elements/link.rb +0 -9
  30. data/lib/watir/elements/radio.rb +1 -1
  31. data/lib/watir/elements/select.rb +22 -7
  32. data/lib/watir/generator/base/spec_extractor.rb +4 -4
  33. data/lib/watir/generator/html/generator.rb +1 -1
  34. data/lib/watir/has_window.rb +17 -15
  35. data/lib/watir/js_execution.rb +3 -3
  36. data/lib/watir/js_snippets.rb +2 -2
  37. data/lib/watir/legacy_wait.rb +1 -1
  38. data/lib/watir/locators.rb +1 -3
  39. data/lib/watir/locators/element/locator.rb +22 -12
  40. data/lib/watir/locators/element/selector_builder.rb +12 -13
  41. data/lib/watir/locators/element/selector_builder/xpath.rb +40 -13
  42. data/lib/watir/locators/text_field/matcher.rb +1 -1
  43. data/lib/watir/locators/text_field/selector_builder/xpath.rb +3 -1
  44. data/lib/watir/logger.rb +7 -20
  45. data/lib/watir/radio_set.rb +2 -2
  46. data/lib/watir/user_editable.rb +6 -2
  47. data/lib/watir/version.rb +1 -1
  48. data/lib/watir/wait.rb +2 -0
  49. data/lib/watir/wait/timer.rb +1 -1
  50. data/lib/watir/window.rb +8 -4
  51. data/lib/watir/window_collection.rb +105 -0
  52. data/lib/watirspec.rb +2 -1
  53. data/lib/watirspec/guards.rb +1 -1
  54. data/lib/watirspec/implementation.rb +3 -5
  55. data/lib/watirspec/rake_tasks.rb +2 -0
  56. data/lib/watirspec/runner.rb +5 -1
  57. data/lib/watirspec/server.rb +1 -1
  58. data/spec/spec_helper.rb +2 -7
  59. data/spec/unit/container_spec.rb +1 -1
  60. data/spec/unit/logger_spec.rb +5 -7
  61. data/spec/unit/match_elements/element_spec.rb +17 -15
  62. data/spec/unit/selector_builder/button_spec.rb +16 -15
  63. data/spec/unit/selector_builder/element_spec.rb +58 -9
  64. data/spec/unit/selector_builder/text_field_spec.rb +14 -14
  65. data/spec/unit/unit_helper.rb +2 -4
  66. data/spec/watirspec/after_hooks_spec.rb +58 -68
  67. data/spec/watirspec/alert_spec.rb +69 -79
  68. data/spec/watirspec/browser_spec.rb +51 -48
  69. data/spec/watirspec/cookies_spec.rb +52 -37
  70. data/spec/watirspec/drag_and_drop_spec.rb +14 -38
  71. data/spec/watirspec/elements/button_spec.rb +2 -0
  72. data/spec/watirspec/elements/buttons_spec.rb +1 -1
  73. data/spec/watirspec/elements/checkbox_spec.rb +8 -4
  74. data/spec/watirspec/elements/date_field_spec.rb +18 -9
  75. data/spec/watirspec/elements/date_time_field_spec.rb +3 -4
  76. data/spec/watirspec/elements/div_spec.rb +62 -54
  77. data/spec/watirspec/elements/element_spec.rb +73 -88
  78. data/spec/watirspec/elements/elements_spec.rb +12 -3
  79. data/spec/watirspec/elements/filefield_spec.rb +25 -50
  80. data/spec/watirspec/elements/form_spec.rb +6 -8
  81. data/spec/watirspec/elements/frame_spec.rb +10 -13
  82. data/spec/watirspec/elements/iframe_spec.rb +12 -9
  83. data/spec/watirspec/elements/iframes_spec.rb +2 -2
  84. data/spec/watirspec/elements/link_spec.rb +23 -12
  85. data/spec/watirspec/elements/links_spec.rb +11 -3
  86. data/spec/watirspec/elements/option_spec.rb +15 -17
  87. data/spec/watirspec/elements/select_list_spec.rb +222 -117
  88. data/spec/watirspec/elements/text_field_spec.rb +8 -4
  89. data/spec/watirspec/elements/tr_spec.rb +0 -9
  90. data/spec/watirspec/html/forms_with_input_elements.html +1 -0
  91. data/spec/watirspec/html/iframes.html +3 -0
  92. data/spec/watirspec/html/non_control_elements.html +4 -4
  93. data/spec/watirspec/html/right_click.html +12 -0
  94. data/spec/watirspec/html/wait.html +6 -6
  95. data/spec/watirspec/html/window_switching.html +10 -0
  96. data/spec/watirspec/legacy_wait_spec.rb +216 -0
  97. data/spec/watirspec/support/rspec_matchers.rb +17 -13
  98. data/spec/watirspec/user_editable_spec.rb +1 -1
  99. data/spec/watirspec/wait_spec.rb +257 -305
  100. data/spec/watirspec/window_switching_spec.rb +332 -211
  101. data/spec/watirspec_helper.rb +16 -19
  102. data/support/doctest_helper.rb +0 -2
  103. data/watir.gemspec +6 -7
  104. metadata +36 -26
  105. data/.travis.yml +0 -84
  106. data/appveyor.yml +0 -12
  107. data/lib/watir/elements/area.rb +0 -10
  108. data/spec/watirspec/relaxed_locate_spec.rb +0 -113
@@ -5,7 +5,7 @@ module Watir
5
5
  private
6
6
 
7
7
  def elements_match?(element, values_to_match)
8
- case element.tag_name.downcase
8
+ case fetch_value(element, :tag_name)
9
9
  when 'input'
10
10
  %i[text label visible_text].each do |key|
11
11
  next unless values_to_match.key?(key)
@@ -36,7 +36,9 @@ module Watir
36
36
 
37
37
  def negative_type_text
38
38
  Watir::TextField::NON_TEXT_TYPES.map { |type|
39
- "#{lhs_for(:type, true)}!=#{SelectorBuilder::XpathSupport.escape type}"
39
+ lhs = lhs_for(:type, downcase: true)
40
+ rhs = SelectorBuilder::XpathSupport.downcase(SelectorBuilder::XpathSupport.escape(type))
41
+ "#{lhs}!=#{rhs}"
40
42
  }.join(' and ')
41
43
  end
42
44
  end
data/lib/watir/logger.rb CHANGED
@@ -2,7 +2,7 @@ require 'forwardable'
2
2
  require 'logger'
3
3
 
4
4
  # Code adapted from Selenium Implementation
5
- # https://github.com/SeleniumHQ/selenium/blob/master/rb/lib/selenium/webdriver/common/logger.rb
5
+ # https://github.com/SeleniumHQ/selenium/blob/trunk/rb/lib/selenium/webdriver/common/logger.rb
6
6
 
7
7
  module Watir
8
8
  #
@@ -25,7 +25,7 @@ module Watir
25
25
  :warn?,
26
26
  :error, :error?,
27
27
  :fatal, :fatal?,
28
- :level
28
+ :level, :level=
29
29
 
30
30
  def initialize(progname = 'Watir')
31
31
  @logger = create_logger($stdout)
@@ -34,8 +34,7 @@ module Watir
34
34
  end
35
35
 
36
36
  def ignore(ids)
37
- ids = [ids] unless ids.is_a? Array
38
- @ignored.concat ids.map(&:to_s)
37
+ @ignored.concat Array(ids).map(&:to_s)
39
38
  end
40
39
 
41
40
  def output=(io)
@@ -51,22 +50,6 @@ module Watir
51
50
  @logger.warn(msg, &block) unless (@ignored & ids).any?
52
51
  end
53
52
 
54
- #
55
- # For Ruby < 2.4 compatibility
56
- # Based on https://github.com/ruby/ruby/blob/ruby_2_3/lib/logger.rb#L250
57
- #
58
-
59
- def level=(severity)
60
- if severity.is_a?(Integer)
61
- @logger.level = severity
62
- else
63
- levels = %w[debug info warn error fatal unknown]
64
- raise ArgumentError, "invalid log level: #{severity}" unless levels.include? severity.to_s.downcase
65
-
66
- @logger.level = severity.to_s.upcase
67
- end
68
- end
69
-
70
53
  #
71
54
  # Returns IO object used by logger internally.
72
55
  #
@@ -96,6 +79,10 @@ module Watir
96
79
  warn "[DEPRECATION] #{msg}#{old} is deprecated. Use #{new} instead#{ref_msg}"
97
80
  end
98
81
 
82
+ def selenium=(val)
83
+ Selenium::WebDriver.logger.level = val
84
+ end
85
+
99
86
  private
100
87
 
101
88
  def create_logger(output)
@@ -4,7 +4,7 @@ module Watir
4
4
  include Exception
5
5
  include Enumerable
6
6
 
7
- delegate %i[exists? present? visible? browser] => :source
7
+ delegate %i[exist? exists? present? visible? browser] => :source
8
8
 
9
9
  attr_reader :source, :frame
10
10
 
@@ -201,7 +201,7 @@ module Watir
201
201
  end
202
202
  alias eql? ==
203
203
 
204
- # Ruby 2.4+ complains about using #delegate to do this
204
+ # Delegating to Private Methods
205
205
  %i[assert_exists element_call].each do |method|
206
206
  define_method(method) do |*args, &blk|
207
207
  source.send(method, *args, &blk)
@@ -21,13 +21,17 @@ module Watir
21
21
  # @param [String, Symbol] args
22
22
  #
23
23
 
24
+ def content_editable
25
+ defined?(@content_editable) && content_editable?
26
+ end
27
+
24
28
  def set!(*args)
25
29
  msg = '#set! does not support special keys, use #set instead'
26
30
  raise ArgumentError, msg if args.any? { |v| v.is_a?(::Symbol) }
27
31
 
28
32
  input_value = args.join
29
33
  set input_value[0]
30
- return content_editable_set!(*args) if @content_editable
34
+ return content_editable_set!(*args) if content_editable
31
35
 
32
36
  element_call { execute_js(:setValue, @element, input_value[0..-2]) }
33
37
  append(input_value[-1])
@@ -43,7 +47,7 @@ module Watir
43
47
  #
44
48
 
45
49
  def append(*args)
46
- raise NotImplementedError, '#append method is not supported with contenteditable element' if @content_editable
50
+ raise NotImplementedError, '#append method is not supported with contenteditable element' if content_editable
47
51
 
48
52
  send_keys(*args)
49
53
  end
data/lib/watir/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Watir
2
- VERSION = '6.16.2'.freeze
2
+ VERSION = '6.18.0'.freeze
3
3
  end
data/lib/watir/wait.rb CHANGED
@@ -229,6 +229,8 @@ module Watir
229
229
  case expected
230
230
  when Regexp
231
231
  expected =~ actual
232
+ when Numeric
233
+ expected == actual
232
234
  else
233
235
  expected.to_s == actual
234
236
  end
@@ -2,7 +2,7 @@ module Watir
2
2
  module Wait
3
3
  class Timer
4
4
  def initialize(timeout: nil)
5
- @end_time = current_time + timeout if timeout
5
+ @end_time = timeout ? current_time + timeout : nil
6
6
  @remaining_time = @end_time - current_time if @end_time
7
7
  end
8
8
 
data/lib/watir/window.rb CHANGED
@@ -6,7 +6,7 @@ module Watir
6
6
 
7
7
  attr_reader :browser
8
8
 
9
- def initialize(browser, selector)
9
+ def initialize(browser, selector = {})
10
10
  @browser = browser
11
11
  @driver = browser.driver
12
12
  @selector = selector
@@ -16,7 +16,7 @@ module Watir
16
16
  elsif selector.key? :handle
17
17
  @handle = selector.delete :handle
18
18
  else
19
- return if selector.keys.all? { |k| %i[title url index].include? k }
19
+ return if selector.keys.all? { |k| %i[title url index element].include? k }
20
20
 
21
21
  raise ArgumentError, "invalid window selector: #{selector_string}"
22
22
  end
@@ -207,6 +207,9 @@ module Watir
207
207
  if @selector.empty?
208
208
  nil
209
209
  elsif @selector.key?(:index)
210
+ Watir.logger.deprecate 'Using :index as a selector for Window', ':title or :url or :element',
211
+ reference: 'http://watir.com/guides/windows/#locating-by-index-is-no-longer-supported',
212
+ ids: [:window_index]
210
213
  @driver.window_handles[Integer(@selector[:index])]
211
214
  else
212
215
  @driver.window_handles.find { |wh| matches?(wh) }
@@ -230,10 +233,11 @@ module Watir
230
233
  @driver.switch_to.window(handle) do
231
234
  matches_title = @selector[:title].nil? || @browser.title =~ /#{@selector[:title]}/
232
235
  matches_url = @selector[:url].nil? || @browser.url =~ /#{@selector[:url]}/
236
+ matches_element = @selector[:element].nil? || @selector[:element].exists?
233
237
 
234
- matches_title && matches_url
238
+ matches_title && matches_url && matches_element
235
239
  end
236
- rescue Selenium::WebDriver::Error::NoSuchWindowError, Selenium::WebDriver::Error::NoSuchDriverError
240
+ rescue Selenium::WebDriver::Error::NoSuchWindowError
237
241
  # the window may disappear while we're iterating.
238
242
  false
239
243
  end
@@ -0,0 +1,105 @@
1
+ module Watir
2
+ class WindowCollection
3
+ include Enumerable
4
+ include Waitable
5
+
6
+ def initialize(browser, selector = {})
7
+ unless selector.keys.all? { |k| %i[title url element].include? k }
8
+ raise ArgumentError, "invalid window selector: #{selector.inspect}"
9
+ end
10
+
11
+ @browser = browser
12
+ @selector = selector
13
+ end
14
+
15
+ #
16
+ # Yields each window in collection.
17
+ #
18
+ # @yieldparam [Watir::Window]
19
+ #
20
+
21
+ def each(&blk)
22
+ reset!
23
+ to_a.each(&blk)
24
+ end
25
+
26
+ alias length count
27
+ alias size count
28
+ alias empty? none?
29
+
30
+ #
31
+ # First window of the collection
32
+ #
33
+ # @note windows in a collection are not ordered so this is not reliably
34
+ # @deprecated use Browser#switch_window or a better Window locator
35
+ # @return [Watir::Window] Returns an instance of a Watir::Window
36
+ #
37
+
38
+ def first
39
+ self[0]
40
+ end
41
+
42
+ #
43
+ # Last window of the collection
44
+ #
45
+ # @note windows in a collection are not ordered so this is not reliably
46
+ # @deprecated use Browser#switch_window or a better Window locator
47
+ # @return [Watir::Window] Returns an instance of a Watir::Window
48
+ #
49
+
50
+ def last
51
+ self[-1]
52
+ end
53
+
54
+ #
55
+ # Get the window at the given index or range.
56
+ #
57
+ # @note windows in a collection are not ordered so this is not reliably
58
+ # @deprecated use Browser#switch_window or a better Window locator
59
+ # @param [Integer, Range] value Index (0-based) or Range of desired window(s)
60
+ # @return [Watir::Window] Returns an instance of a Watir::Window
61
+ #
62
+
63
+ def [](value)
64
+ old = 'using indexing with windows'
65
+ new = 'Browser#switch_window or Browser#window with :title, :url or :element selectors'
66
+ reference = 'http://watir.com/window_indexes'
67
+ Watir.logger.deprecate old, new, reference: reference, ids: [:window_index]
68
+
69
+ to_a[value]
70
+ end
71
+
72
+ def ==(other)
73
+ to_a == other.to_a
74
+ end
75
+ alias eql? ==
76
+
77
+ def to_a
78
+ @to_a ||= begin
79
+ handles = @browser.driver.window_handles.select { |wh| matches?(wh) }
80
+ handles.map { |wh| Window.new(@browser, handle: wh) }
81
+ end
82
+ end
83
+
84
+ def reset!
85
+ @to_a = nil
86
+ end
87
+
88
+ private
89
+
90
+ # NOTE: This is the exact same code from `Window#matches?`
91
+ # TODO: Move this code into a separate WindowLocator class
92
+ def matches?(handle)
93
+ @selector.empty? || @browser.driver.switch_to.window(handle) do
94
+ matches_title = @selector[:title].nil? || @browser.title =~ /#{@selector[:title]}/
95
+ matches_url = @selector[:url].nil? || @browser.url =~ /#{@selector[:url]}/
96
+ matches_element = @selector[:element].nil? || @selector[:element].exists?
97
+
98
+ matches_title && matches_url && matches_element
99
+ end
100
+ rescue Selenium::WebDriver::Error::NoSuchWindowError
101
+ # the window may disappear while we're iterating.
102
+ false
103
+ end
104
+ end # Window
105
+ end # Watir
data/lib/watirspec.rb CHANGED
@@ -31,7 +31,7 @@ module WatirSpec
31
31
 
32
32
  def load_support
33
33
  root = File.expand_path('../spec/watirspec', __dir__)
34
- Dir.glob("#{root}/support/**/*.rb").each do |file|
34
+ Dir.glob("#{root}/support/**/*.rb").sort.each do |file|
35
35
  require file
36
36
  end
37
37
  end
@@ -77,6 +77,7 @@ module WatirSpec
77
77
 
78
78
  info << caps.browser_name.to_s
79
79
  info << caps.version.to_s
80
+ info << @implementation.driver_info
80
81
 
81
82
  Watir.logger.warn "running watirspec against #{info.join ' '} using:\n#{WatirSpec.implementation.inspect_args}",
82
83
  ids: [:browser_info]
@@ -17,7 +17,7 @@ module WatirSpec
17
17
  "\tnone."
18
18
  else
19
19
  gs.each do |guard|
20
- guard[:data][:file] = guard[:data][:file][%r{\/spec\/(.*):}, 1]
20
+ guard[:data][:file] = guard[:data][:file][%r{/spec/(.*):}, 1]
21
21
  guard_name = "#{guard[:name]}:".ljust(15)
22
22
  str << " \t#{guard_name} #{guard[:data].inspect}\n"
23
23
  end
@@ -1,16 +1,14 @@
1
1
  module WatirSpec
2
2
  class Implementation
3
3
  attr_writer :name, :guard_proc, :browser_class
4
- attr_accessor :browser_args
4
+ attr_accessor :browser_args, :driver_info
5
5
 
6
6
  def initialize
7
7
  @guard_proc = nil
8
8
  end
9
9
 
10
- def initialize_copy(orig)
11
- super
12
- # Backward compatibility < Ruby 2.4
13
- @browser_args = browser_args.map { |arg| arg.is_a?(Symbol) ? arg : arg.dup }
10
+ def initialize_copy(_orig)
11
+ @browser_args = browser_args.map(&:dup)
14
12
  end
15
13
 
16
14
  def browser_class
@@ -3,6 +3,8 @@ require 'rake/tasklib'
3
3
  module WatirSpec
4
4
  class RakeTasks < Rake::TaskLib
5
5
  def initialize
6
+ super
7
+
6
8
  namespace :watirspec do
7
9
  desc 'Initialize WatirSpec'
8
10
  task :init do
@@ -10,6 +10,10 @@ module WatirSpec
10
10
  def messages
11
11
  browser.div(id: 'messages').divs.map(&:text)
12
12
  end
13
+
14
+ def event_log
15
+ browser.div(id: 'log').wait_until(&:present?).ps.map(&:text)
16
+ end
13
17
  end
14
18
 
15
19
  module_function
@@ -29,7 +33,7 @@ module WatirSpec
29
33
  end
30
34
 
31
35
  def execute_if_necessary
32
- execute if !@executed && @execute
36
+ execute if (!defined?(@executed) || !@executed) && @execute
33
37
  end
34
38
 
35
39
  def configure
@@ -24,7 +24,7 @@ module WatirSpec
24
24
  private
25
25
 
26
26
  def running?
27
- @running
27
+ defined?(@running) && @running
28
28
  end
29
29
 
30
30
  def run_server
data/spec/spec_helper.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  $LOAD_PATH.unshift(File.dirname(__FILE__))
2
2
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
3
 
4
- if ENV['TRAVIS'] || ENV['COVERAGE']
4
+ if ENV['GITHUB_ACTIONS'] || ENV['COVERAGE']
5
5
  require 'coveralls'
6
6
  require 'simplecov'
7
7
  require 'simplecov-console'
@@ -19,11 +19,10 @@ if ENV['TRAVIS'] || ENV['COVERAGE']
19
19
  end
20
20
 
21
21
  require 'watir'
22
- require 'webdrivers'
23
22
  require 'locator_spec_helper'
24
23
  require 'rspec'
25
24
 
26
- if ENV['TRAVIS']
25
+ if ENV['GITHUB_ACTIONS']
27
26
  require 'rspec/retry'
28
27
  RSpec.configure do |config|
29
28
  config.verbose_retry = true
@@ -36,7 +35,3 @@ end
36
35
  SELENIUM_SELECTORS = %i[css tag_name xpath link_text partial_link_text link].freeze
37
36
 
38
37
  Watir.relaxed_locate = false if ENV['RELAXED_LOCATE'] == 'false'
39
-
40
- ENV['DISPLAY'] = ':99.0' if ENV['TRAVIS']
41
-
42
- raise 'DISPLAY not set' if Selenium::WebDriver::Platform.linux? && ENV['DISPLAY'].nil?
@@ -17,7 +17,7 @@ describe Watir::Container do
17
17
  end
18
18
 
19
19
  it 'returns the hash given' do
20
- expect(@container.public_extract_selector([how: 'what'])).to eq Hash[how: 'what']
20
+ expect(@container.public_extract_selector([{how: 'what'}])).to eq Hash[how: 'what']
21
21
  end
22
22
 
23
23
  it 'returns an empty hash if given no args' do
@@ -34,13 +34,11 @@ module Watir
34
34
  end
35
35
 
36
36
  it 'allows to output to file' do
37
- begin
38
- Watir.logger.output = 'test.log'
39
- Watir.logger.warn('message')
40
- expect(File.read('test.log')).to include('WARN Watir message')
41
- ensure
42
- File.delete('test.log')
43
- end
37
+ Watir.logger.output = 'test.log'
38
+ Watir.logger.warn('message')
39
+ expect(File.read('test.log')).to include('WARN Watir message')
40
+ ensure
41
+ File.delete('test.log')
44
42
  end
45
43
 
46
44
  it 'allows to deprecate functionality' do