watir 6.16.2 → 6.18.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/actions/enable-safari/action.yml +11 -0
- data/.github/actions/install-chrome/action.yml +11 -0
- data/.github/workflows/linux.yml +61 -0
- data/.github/workflows/mac.yml +55 -0
- data/.github/workflows/unit.yml +31 -0
- data/.github/workflows/windows.yml +39 -0
- data/.rubocop.yml +32 -107
- data/.rubocop_todo.yml +36 -0
- data/CHANGES.md +42 -0
- data/Gemfile +3 -1
- data/LICENSE +2 -2
- data/README.md +9 -10
- data/Rakefile +4 -4
- data/lib/watir-webdriver.rb +1 -1
- data/lib/watir.rb +1 -1
- data/lib/watir/adjacent.rb +8 -10
- data/lib/watir/after_hooks.rb +4 -4
- data/lib/watir/alert.rb +1 -0
- data/lib/watir/attribute_helper.rb +2 -0
- data/lib/watir/browser.rb +7 -3
- data/lib/watir/capabilities.rb +9 -6
- data/lib/watir/cookies.rb +3 -1
- data/lib/watir/element_collection.rb +21 -6
- data/lib/watir/elements/element.rb +66 -53
- data/lib/watir/elements/file_field.rb +1 -0
- data/lib/watir/elements/html_elements.rb +0 -1
- data/lib/watir/elements/iframe.rb +4 -3
- data/lib/watir/elements/link.rb +0 -9
- data/lib/watir/elements/radio.rb +1 -1
- data/lib/watir/elements/select.rb +22 -7
- data/lib/watir/generator/base/spec_extractor.rb +4 -4
- data/lib/watir/generator/html/generator.rb +1 -1
- data/lib/watir/has_window.rb +17 -15
- data/lib/watir/js_execution.rb +3 -3
- data/lib/watir/js_snippets.rb +2 -2
- data/lib/watir/legacy_wait.rb +1 -1
- data/lib/watir/locators.rb +1 -3
- data/lib/watir/locators/element/locator.rb +22 -12
- data/lib/watir/locators/element/selector_builder.rb +12 -13
- data/lib/watir/locators/element/selector_builder/xpath.rb +40 -13
- data/lib/watir/locators/text_field/matcher.rb +1 -1
- data/lib/watir/locators/text_field/selector_builder/xpath.rb +3 -1
- data/lib/watir/logger.rb +7 -20
- data/lib/watir/radio_set.rb +2 -2
- data/lib/watir/user_editable.rb +6 -2
- data/lib/watir/version.rb +1 -1
- data/lib/watir/wait.rb +2 -0
- data/lib/watir/wait/timer.rb +1 -1
- data/lib/watir/window.rb +8 -4
- data/lib/watir/window_collection.rb +105 -0
- data/lib/watirspec.rb +2 -1
- data/lib/watirspec/guards.rb +1 -1
- data/lib/watirspec/implementation.rb +3 -5
- data/lib/watirspec/rake_tasks.rb +2 -0
- data/lib/watirspec/runner.rb +5 -1
- data/lib/watirspec/server.rb +1 -1
- data/spec/spec_helper.rb +2 -7
- data/spec/unit/container_spec.rb +1 -1
- data/spec/unit/logger_spec.rb +5 -7
- data/spec/unit/match_elements/element_spec.rb +17 -15
- data/spec/unit/selector_builder/button_spec.rb +16 -15
- data/spec/unit/selector_builder/element_spec.rb +58 -9
- data/spec/unit/selector_builder/text_field_spec.rb +14 -14
- data/spec/unit/unit_helper.rb +2 -4
- data/spec/watirspec/after_hooks_spec.rb +58 -68
- data/spec/watirspec/alert_spec.rb +69 -79
- data/spec/watirspec/browser_spec.rb +51 -48
- data/spec/watirspec/cookies_spec.rb +52 -37
- data/spec/watirspec/drag_and_drop_spec.rb +14 -38
- data/spec/watirspec/elements/button_spec.rb +2 -0
- data/spec/watirspec/elements/buttons_spec.rb +1 -1
- data/spec/watirspec/elements/checkbox_spec.rb +8 -4
- data/spec/watirspec/elements/date_field_spec.rb +18 -9
- data/spec/watirspec/elements/date_time_field_spec.rb +3 -4
- data/spec/watirspec/elements/div_spec.rb +62 -54
- data/spec/watirspec/elements/element_spec.rb +73 -88
- data/spec/watirspec/elements/elements_spec.rb +12 -3
- data/spec/watirspec/elements/filefield_spec.rb +25 -50
- data/spec/watirspec/elements/form_spec.rb +6 -8
- data/spec/watirspec/elements/frame_spec.rb +10 -13
- data/spec/watirspec/elements/iframe_spec.rb +12 -9
- data/spec/watirspec/elements/iframes_spec.rb +2 -2
- data/spec/watirspec/elements/link_spec.rb +23 -12
- data/spec/watirspec/elements/links_spec.rb +11 -3
- data/spec/watirspec/elements/option_spec.rb +15 -17
- data/spec/watirspec/elements/select_list_spec.rb +222 -117
- data/spec/watirspec/elements/text_field_spec.rb +8 -4
- data/spec/watirspec/elements/tr_spec.rb +0 -9
- data/spec/watirspec/html/forms_with_input_elements.html +1 -0
- data/spec/watirspec/html/iframes.html +3 -0
- data/spec/watirspec/html/non_control_elements.html +4 -4
- data/spec/watirspec/html/right_click.html +12 -0
- data/spec/watirspec/html/wait.html +6 -6
- data/spec/watirspec/html/window_switching.html +10 -0
- data/spec/watirspec/legacy_wait_spec.rb +216 -0
- data/spec/watirspec/support/rspec_matchers.rb +17 -13
- data/spec/watirspec/user_editable_spec.rb +1 -1
- data/spec/watirspec/wait_spec.rb +257 -305
- data/spec/watirspec/window_switching_spec.rb +332 -211
- data/spec/watirspec_helper.rb +16 -19
- data/support/doctest_helper.rb +0 -2
- data/watir.gemspec +6 -7
- metadata +36 -26
- data/.travis.yml +0 -84
- data/appveyor.yml +0 -12
- data/lib/watir/elements/area.rb +0 -10
- data/spec/watirspec/relaxed_locate_spec.rb +0 -113
data/CHANGES.md
CHANGED
@@ -1,3 +1,45 @@
|
|
1
|
+
### 6.18.0 (2021-02-26)
|
2
|
+
|
3
|
+
* Implement `WindowCollection` to manage multiple `Window` objects
|
4
|
+
* Add support for locating `Window` by `:element`
|
5
|
+
* Deprecate locating `Window` by `:index`
|
6
|
+
* Deprecate `Select#select_all` in favor of `#select` and an `Array`
|
7
|
+
* Implement `Browser#switch_window` (#849)
|
8
|
+
* Add support for `Numeric` attribute values to `Waitable`
|
9
|
+
* Allow users to specify Selenium 4 in their projects
|
10
|
+
* Update stale element handling behavior to match webdriver spec (#905 #909)
|
11
|
+
* Implement `Waitable` for `ElementCollection` (#853 #857)
|
12
|
+
* Improve performance for nested elements (#843)
|
13
|
+
* Less strict version check for `regexp_parser` gem (thanks Pavel Lobashov)
|
14
|
+
|
15
|
+
### 6.17.0 (2020-08-27)
|
16
|
+
* Require Ruby > 2.5
|
17
|
+
* Implement Logger#selenium= to set selenium level from Watir
|
18
|
+
* Implement FileField#upload
|
19
|
+
* Fix bug with staleness handling in #exist and #present? (#853 & #852)
|
20
|
+
* Fix bug when locating elements by text with RegExp (#866 #871)
|
21
|
+
* Implement modifiers for `Element#right_click` (thanks Lakshya Kapoor #861)
|
22
|
+
* Updated locator code to adhere to spec on what attributes are case sensitive (#507 #856)
|
23
|
+
* Fix locating bug when iframe is nested under another element (thanks Matthew Mazaika #885 #886)
|
24
|
+
* Deprecate Element#scroll_into_view in favor of the new Scroll methods (#884)
|
25
|
+
* Fix threading bugs by allowing each Browser instance its own Timer (#881)
|
26
|
+
* Allow adjacent locators to return Input subtype when applicable (#878)
|
27
|
+
* Removed unnecessary reference to rubyforge (thanks olleolleolle #874)
|
28
|
+
* Removed deprecated Selenium classes (thanks joesho112358 #867)
|
29
|
+
* Add support for :service parameter for initializing Browser
|
30
|
+
|
31
|
+
### 6.16.5 (2018-12-25)
|
32
|
+
|
33
|
+
* Fix bug with nested elements using scopes (#842)
|
34
|
+
|
35
|
+
### 6.16.4 (2018-12-24)
|
36
|
+
|
37
|
+
* Minor adjustments to support locator extensions
|
38
|
+
|
39
|
+
### 6.16.3 (2018-12-24)
|
40
|
+
|
41
|
+
* Minor adjustments to support locator extensions
|
42
|
+
|
1
43
|
### 6.16.2 (2018-12-24)
|
2
44
|
|
3
45
|
* Fix bug merging scope when locating nested elements with css locator (#841)
|
data/Gemfile
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
-
source '
|
1
|
+
source 'https://rubygems.org'
|
2
2
|
|
3
3
|
gem 'webidl', path: File.expand_path('../webidl') if ENV['LOCAL_WEBIDL']
|
4
4
|
|
5
5
|
gem 'selenium-webdriver', path: File.expand_path('../selenium/build/rb') if ENV['LOCAL_SELENIUM']
|
6
6
|
|
7
|
+
gem 'ffi' if Gem.win_platform? # For selenium-webdriver on Windows
|
8
|
+
|
7
9
|
# Specify your gem's dependencies in watir.gemspec
|
8
10
|
gemspec
|
data/LICENSE
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
(the MIT License)
|
2
2
|
|
3
3
|
Copyright (c) 2009-2015 Jari Bakken
|
4
|
-
Copyright (c) 2015-
|
5
|
-
Copyright (c) 2018 Justin Ko
|
4
|
+
Copyright (c) 2015-2021 Alex Rodionov, Titus Fortner
|
5
|
+
Copyright (c) 2018-2021 Justin Ko
|
6
6
|
|
7
7
|
Permission is hereby granted, free of charge, to any person obtaining
|
8
8
|
a copy of this software and associated documentation files (the
|
data/README.md
CHANGED
@@ -3,10 +3,12 @@
|
|
3
3
|
Watir Powered By Selenium!
|
4
4
|
|
5
5
|
[![Gem Version](https://badge.fury.io/rb/watir.svg)](http://badge.fury.io/rb/watir)
|
6
|
-
[![
|
7
|
-
[![
|
6
|
+
[![Unit Tests](https://github.com/titusfortner/watir/workflows/Unit%20Tests/badge.svg)](https://github.com/watir/watir/actions?query=workflow%3A%22Unit+Tests%22)
|
7
|
+
[![Mac Tests](https://github.com/titusfortner/watir/workflows/Mac%20Tests/badge.svg)](https://github.com/watir/watir/actions?query=workflow%3A%22Mac+Tests%22)
|
8
|
+
[![Windows Tests](https://github.com/titusfortner/watir/workflows/Windows%20Tests/badge.svg)](https://github.com/watir/watir/actions?query=workflow%3A%22Windows+Tests%22)
|
9
|
+
[![Linux Tests](https://github.com/titusfortner/watir/workflows/Linux%20Tests/badge.svg)](https://github.com/watir/watir/actions?query=workflow%3A%22Linux+Tests%22)
|
8
10
|
[![Code Climate](https://codeclimate.com/github/watir/watir.svg)](https://codeclimate.com/github/watir/watir)
|
9
|
-
[![Coverage Status](https://coveralls.io/repos/github/watir/watir/badge.svg?branch=
|
11
|
+
[![Coverage Status](https://coveralls.io/repos/github/watir/watir/badge.svg?branch=main)](https://coveralls.io/github/watir/watir?branch=master)
|
10
12
|
|
11
13
|
## Using Watir
|
12
14
|
|
@@ -69,11 +71,11 @@ $ bundle exec rake svg:update
|
|
69
71
|
|
70
72
|
## Specs
|
71
73
|
|
72
|
-
####
|
74
|
+
#### Github Actions
|
73
75
|
|
74
|
-
Watir specs are run
|
76
|
+
Watir specs are run with [Github Actions](https://github.com/watir/watir/workflows).
|
75
77
|
|
76
|
-
Watir code is tested
|
78
|
+
Watir code is tested on Linux with latest versions of supported browsers and all active Ruby versions.
|
77
79
|
|
78
80
|
#### Doctests
|
79
81
|
|
@@ -95,7 +97,7 @@ to ensure all paths in their code have tests associated with them.
|
|
95
97
|
|
96
98
|
Watir is using [Rubocop](https://github.com/rubocop-hq/rubocop) to ensure a consistent style across the
|
97
99
|
code base. It is run with our minimum supported Ruby version (2.3)
|
98
|
-
We have some [established exceptions](https://github.com/watir/watir/blob/
|
100
|
+
We have some [established exceptions](https://github.com/watir/watir/blob/main/.rubocop.yml)
|
99
101
|
that might need to be tweaked for new code submissions. This can be addressed in the PR as necessary.
|
100
102
|
|
101
103
|
#### Statistics
|
@@ -107,7 +109,4 @@ on wire calls.
|
|
107
109
|
|
108
110
|
## Copyright
|
109
111
|
|
110
|
-
Copyright (c) 2009-2015 Jari Bakken
|
111
|
-
Copyright (c) 2015-2018 Alex Rodionov, Titus Fortner
|
112
|
-
Copyright (c) 2018 Justin Ko
|
113
112
|
See LICENSE for details
|
data/Rakefile
CHANGED
@@ -5,7 +5,7 @@ Bundler::GemHelper.install_tasks
|
|
5
5
|
|
6
6
|
require 'rspec/core/rake_task'
|
7
7
|
RSpec::Core::RakeTask.new(:spec) do |spec|
|
8
|
-
spec.rspec_opts = %w[--color --
|
8
|
+
spec.rspec_opts = %w[--color --format doc]
|
9
9
|
spec.pattern = 'spec/**/*_spec.rb'
|
10
10
|
spec.exclude_pattern = 'spec/unit/**/*_spec.rb'
|
11
11
|
end
|
@@ -61,7 +61,7 @@ end
|
|
61
61
|
|
62
62
|
if extractor.errors.any?
|
63
63
|
puts "\n\n<======================= ERRORS =======================>\n\n"
|
64
|
-
puts extractor.errors.join("\n
|
64
|
+
puts extractor.errors.join("\n#{'=' * 80}\n")
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
@@ -93,7 +93,7 @@ YARD::Rake::YardocTask.new do |task|
|
|
93
93
|
task.options = %w[--debug] # this is pretty slow, so nice with some output
|
94
94
|
end
|
95
95
|
|
96
|
-
require 'yard
|
96
|
+
require 'yard/doctest/rake'
|
97
97
|
YARD::Doctest::RakeTask.new do |task|
|
98
98
|
task.doctest_opts = ['-v']
|
99
99
|
end
|
@@ -115,7 +115,7 @@ namespace :changes do
|
|
115
115
|
|
116
116
|
desc 'Print latest diff'
|
117
117
|
task print: :differ do
|
118
|
-
VersionDiffer.new.print_latest(
|
118
|
+
VersionDiffer.new.print_latest($stdout)
|
119
119
|
end
|
120
120
|
end
|
121
121
|
|
data/lib/watir-webdriver.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
require 'watir'
|
2
|
-
warn Kernel.caller.first
|
2
|
+
warn "#{Kernel.caller.first}: `require 'watir-webdriver'` is deprecated. Please, use `require 'watir'`."
|
data/lib/watir.rb
CHANGED
@@ -6,6 +6,7 @@ require 'watir/legacy_wait'
|
|
6
6
|
require 'watir/wait'
|
7
7
|
require 'watir/exception'
|
8
8
|
require 'watir/window'
|
9
|
+
require 'watir/window_collection'
|
9
10
|
require 'watir/has_window'
|
10
11
|
require 'watir/adjacent'
|
11
12
|
require 'watir/js_execution'
|
@@ -114,7 +115,6 @@ require 'watir/elements/element'
|
|
114
115
|
require 'watir/elements/html_elements'
|
115
116
|
require 'watir/elements/svg_elements'
|
116
117
|
|
117
|
-
require 'watir/elements/area'
|
118
118
|
require 'watir/elements/button'
|
119
119
|
require 'watir/elements/cell'
|
120
120
|
require 'watir/elements/checkbox'
|
data/lib/watir/adjacent.rb
CHANGED
@@ -113,16 +113,14 @@ module Watir
|
|
113
113
|
def xpath_adjacent(opt = {})
|
114
114
|
plural = opt.delete(:plural)
|
115
115
|
opt[:index] ||= 0 unless plural || opt.values.any? { |e| e.is_a? Regexp }
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
end
|
125
|
-
klass.new(self, opt)
|
116
|
+
if !plural
|
117
|
+
el = Watir.element_class_for(opt[:tag_name] || '').new(self, opt)
|
118
|
+
el.is_a?(Input) ? el.to_subtype : el
|
119
|
+
elsif opt[:tag_name]
|
120
|
+
Watir.const_get("#{Watir.element_class_for(opt[:tag_name])}Collection").new(self, opt)
|
121
|
+
else
|
122
|
+
HTMLElementCollection.new(self, opt)
|
123
|
+
end
|
126
124
|
end
|
127
125
|
end # Adjacent
|
128
126
|
end # Watir
|
data/lib/watir/after_hooks.rb
CHANGED
@@ -69,8 +69,8 @@ module Watir
|
|
69
69
|
return unless @after_hooks.any? && !@browser.alert.exists?
|
70
70
|
|
71
71
|
each { |after_hook| after_hook.call(@browser) }
|
72
|
-
rescue Selenium::WebDriver::Error::NoSuchWindowError =>
|
73
|
-
Watir.logger.info "Could not execute After Hooks because browser window was closed #{
|
72
|
+
rescue Selenium::WebDriver::Error::NoSuchWindowError => e
|
73
|
+
Watir.logger.info "Could not execute After Hooks because browser window was closed #{e}"
|
74
74
|
end
|
75
75
|
|
76
76
|
#
|
@@ -99,8 +99,8 @@ module Watir
|
|
99
99
|
# @yieldparam [#call] after_hook Object responding to call
|
100
100
|
#
|
101
101
|
|
102
|
-
def each
|
103
|
-
@after_hooks.each { |after_hook|
|
102
|
+
def each(&blk)
|
103
|
+
@after_hooks.each { |after_hook| blk.call(after_hook) }
|
104
104
|
end
|
105
105
|
|
106
106
|
#
|
data/lib/watir/alert.rb
CHANGED
data/lib/watir/browser.rb
CHANGED
@@ -11,7 +11,7 @@ module Watir
|
|
11
11
|
include Exception
|
12
12
|
include Scrolling
|
13
13
|
|
14
|
-
attr_writer :default_context, :original_window, :locator_namespace
|
14
|
+
attr_writer :default_context, :original_window, :locator_namespace, :timer
|
15
15
|
attr_reader :driver, :after_hooks
|
16
16
|
alias wd driver # ensures duck typing with Watir::Element
|
17
17
|
|
@@ -213,11 +213,11 @@ module Watir
|
|
213
213
|
# @param args Arguments will be available in the given script in the 'arguments' pseudo-array
|
214
214
|
#
|
215
215
|
|
216
|
-
def execute_script(script, *args)
|
216
|
+
def execute_script(script, *args, function_name: nil)
|
217
217
|
args.map! do |e|
|
218
218
|
e.is_a?(Element) ? e.wait_until(&:exists?).wd : e
|
219
219
|
end
|
220
|
-
|
220
|
+
Watir.logger.info "Executing Script on Browser: #{function_name}" if function_name
|
221
221
|
wrap_elements_in(self, @driver.execute_script(script, *args))
|
222
222
|
end
|
223
223
|
|
@@ -300,6 +300,10 @@ module Watir
|
|
300
300
|
end
|
301
301
|
end
|
302
302
|
|
303
|
+
def timer
|
304
|
+
@timer ||= Wait::Timer.new
|
305
|
+
end
|
306
|
+
|
303
307
|
private
|
304
308
|
|
305
309
|
def wrap_element(scope, element)
|
data/lib/watir/capabilities.rb
CHANGED
@@ -25,7 +25,11 @@ module Watir
|
|
25
25
|
|
26
26
|
def process_arguments
|
27
27
|
url = @options.delete(:url)
|
28
|
-
|
28
|
+
if url
|
29
|
+
@selenium_opts[:url] = url
|
30
|
+
elsif @options.key?(:service)
|
31
|
+
@selenium_opts[:service] = options.delete(:service)
|
32
|
+
end
|
29
33
|
|
30
34
|
create_http_client
|
31
35
|
|
@@ -63,7 +67,6 @@ module Watir
|
|
63
67
|
end
|
64
68
|
|
65
69
|
# TODO: - this will get addressed with Capabilities Update
|
66
|
-
# rubocop:disable Metrics/AbcSize
|
67
70
|
# rubocop:disable Metrics/MethodLength
|
68
71
|
# rubocop:disable Metrics/PerceivedComplexity:
|
69
72
|
# rubocop:disable Metrics/CyclomaticComplexity::
|
@@ -82,7 +85,7 @@ module Watir
|
|
82
85
|
browser_options[:args] += ['--headless', '--disable-gpu']
|
83
86
|
end
|
84
87
|
@selenium_opts[:options] = browser_options if browser_options.is_a? Selenium::WebDriver::Chrome::Options
|
85
|
-
@selenium_opts[:options] ||= Selenium::WebDriver::Chrome::Options.new(browser_options)
|
88
|
+
@selenium_opts[:options] ||= Selenium::WebDriver::Chrome::Options.new(**browser_options)
|
86
89
|
when :firefox
|
87
90
|
profile = @options.delete(:profile)
|
88
91
|
if browser_options.is_a? Selenium::WebDriver::Firefox::Options
|
@@ -97,7 +100,7 @@ module Watir
|
|
97
100
|
browser_options[:args] ||= []
|
98
101
|
browser_options[:args] += ['--headless']
|
99
102
|
end
|
100
|
-
@selenium_opts[:options] ||= Selenium::WebDriver::Firefox::Options.new(browser_options)
|
103
|
+
@selenium_opts[:options] ||= Selenium::WebDriver::Firefox::Options.new(**browser_options)
|
101
104
|
@selenium_opts[:options].profile = profile if profile
|
102
105
|
when :safari
|
103
106
|
Selenium::WebDriver::Safari.technology_preview! if @options.delete(:technology_preview)
|
@@ -120,13 +123,13 @@ module Watir
|
|
120
123
|
end
|
121
124
|
unless browser_options.is_a? Selenium::WebDriver::IE::Options
|
122
125
|
ie_caps = browser_options.select { |k| Selenium::WebDriver::IE::Options::CAPABILITIES.include?(k) }
|
123
|
-
browser_options = Selenium::WebDriver::IE::Options.new(browser_options)
|
126
|
+
browser_options = Selenium::WebDriver::IE::Options.new(**browser_options)
|
124
127
|
ie_caps.each { |k, v| browser_options.add_option(k, v) }
|
125
128
|
end
|
126
129
|
@selenium_opts[:options] = browser_options
|
127
130
|
end
|
128
131
|
end
|
129
|
-
|
132
|
+
|
130
133
|
# rubocop:enable Metrics/MethodLength
|
131
134
|
# rubocop:enable Metrics/PerceivedComplexity:
|
132
135
|
# rubocop:enable Metrics/CyclomaticComplexity::
|
data/lib/watir/cookies.rb
CHANGED
@@ -61,7 +61,7 @@ module Watir
|
|
61
61
|
cookie[:path] = opts[:path] if opts.key?(:path)
|
62
62
|
expires = opts[:expires]
|
63
63
|
if expires
|
64
|
-
cookie[:expires] =
|
64
|
+
cookie[:expires] = expires.is_a?(String) ? ::Time.parse(expires) : expires
|
65
65
|
end
|
66
66
|
cookie[:domain] = opts[:domain] if opts.key?(:domain)
|
67
67
|
|
@@ -105,6 +105,8 @@ module Watir
|
|
105
105
|
IO.write(file, to_a.to_yaml)
|
106
106
|
end
|
107
107
|
|
108
|
+
#
|
109
|
+
# TODO: Use :permitted_classes keyword when minimum supported Ruby is 2.6
|
108
110
|
#
|
109
111
|
# Load cookies from file
|
110
112
|
#
|
@@ -7,17 +7,19 @@ module Watir
|
|
7
7
|
include Enumerable
|
8
8
|
include Exception
|
9
9
|
include JSSnippets
|
10
|
+
include Waitable
|
10
11
|
include Locators::ClassHelpers
|
11
12
|
|
12
13
|
def initialize(query_scope, selector)
|
13
14
|
@query_scope = query_scope
|
14
15
|
@selector = selector
|
16
|
+
@to_a = nil
|
15
17
|
|
16
18
|
build unless @selector.key?(:element)
|
17
19
|
end
|
18
20
|
|
19
21
|
#
|
20
|
-
#
|
22
|
+
# Relocates elements then yields each element in resulting collection.
|
21
23
|
#
|
22
24
|
# @example
|
23
25
|
# divs = browser.divs(class: 'kls')
|
@@ -29,6 +31,7 @@ module Watir
|
|
29
31
|
#
|
30
32
|
|
31
33
|
def each(&blk)
|
34
|
+
reset!
|
32
35
|
to_a.each(&blk)
|
33
36
|
end
|
34
37
|
|
@@ -37,6 +40,9 @@ module Watir
|
|
37
40
|
|
38
41
|
alias empty? none?
|
39
42
|
|
43
|
+
alias exist? any?
|
44
|
+
alias exists? any?
|
45
|
+
|
40
46
|
def build
|
41
47
|
selector_builder.build(@selector.dup)
|
42
48
|
end
|
@@ -145,14 +151,19 @@ module Watir
|
|
145
151
|
alias eql? ==
|
146
152
|
|
147
153
|
#
|
148
|
-
#
|
154
|
+
# Removes cache of previously located elements in the collection.
|
149
155
|
#
|
150
156
|
# @example
|
151
157
|
# options = browser.select_list(name: "new_user_languages").options
|
152
|
-
#
|
153
|
-
#
|
158
|
+
# options.reset!
|
159
|
+
# options[0]
|
160
|
+
# #=> nil
|
154
161
|
#
|
155
162
|
|
163
|
+
def reset!
|
164
|
+
@to_a = nil
|
165
|
+
end
|
166
|
+
|
156
167
|
private
|
157
168
|
|
158
169
|
def elements
|
@@ -182,7 +193,11 @@ module Watir
|
|
182
193
|
end
|
183
194
|
|
184
195
|
def ensure_context
|
185
|
-
|
196
|
+
if @query_scope.is_a?(Browser) || !@query_scope.located? && @query_scope.is_a?(IFrame)
|
197
|
+
@query_scope.browser.locate
|
198
|
+
elsif @query_scope.located? && @query_scope.stale?
|
199
|
+
@query_scope.locate
|
200
|
+
end
|
186
201
|
@query_scope.switch_to! if @query_scope.is_a?(IFrame)
|
187
202
|
end
|
188
203
|
|
@@ -191,7 +206,7 @@ module Watir
|
|
191
206
|
end
|
192
207
|
|
193
208
|
def element_class
|
194
|
-
|
209
|
+
Watir.const_get(self.class.name.sub(/Collection$/, ''))
|
195
210
|
end
|
196
211
|
|
197
212
|
def construct_subtype(element, hash, tag_name)
|
@@ -18,6 +18,17 @@ module Watir
|
|
18
18
|
attr_accessor :keyword
|
19
19
|
attr_reader :selector
|
20
20
|
|
21
|
+
# https://www.w3.org/TR/html52/single-page.html#casesensitivity
|
22
|
+
CASE_INSENSITIVE_ATTRIBUTES = %i[accept accept_charset align alink axis
|
23
|
+
bgcolor charset checked clear codetype
|
24
|
+
color compact declare defer dir direction
|
25
|
+
disabled enctype face frame hreflang
|
26
|
+
http_equiv lang language link media
|
27
|
+
method multiple nohref noresize noshade
|
28
|
+
nowrap readonly rel rev rules scope
|
29
|
+
scrolling selected shape target text
|
30
|
+
type valign valuetype vlink].freeze
|
31
|
+
|
21
32
|
#
|
22
33
|
# temporarily add :id and :class_name manually since they're no longer specified in the HTML spec.
|
23
34
|
#
|
@@ -54,12 +65,9 @@ module Watir
|
|
54
65
|
|
55
66
|
def exists?
|
56
67
|
if located? && stale?
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
ids: [:stale_exists]
|
61
|
-
# TODO: Change this to `reset!` after removing deprecation
|
62
|
-
return false
|
68
|
+
reset!
|
69
|
+
elsif located?
|
70
|
+
return true
|
63
71
|
end
|
64
72
|
|
65
73
|
assert_exists
|
@@ -141,7 +149,6 @@ module Watir
|
|
141
149
|
#
|
142
150
|
|
143
151
|
def click(*modifiers)
|
144
|
-
# TODO: Should wait_for_enabled be default, or `Button` specific behavior?
|
145
152
|
element_call(:wait_for_enabled) do
|
146
153
|
if modifiers.any?
|
147
154
|
action = driver.action
|
@@ -196,15 +203,35 @@ module Watir
|
|
196
203
|
end
|
197
204
|
|
198
205
|
#
|
199
|
-
# Right clicks the element.
|
200
|
-
# Note that
|
206
|
+
# Right clicks the element, optionally while pressing the given modifier keys.
|
207
|
+
# Note that support for holding a modifier key is currently experimental,
|
208
|
+
# and may not work at all. Also, the browser support may vary.
|
201
209
|
#
|
202
210
|
# @example
|
203
211
|
# browser.element(name: "new_user_button").right_click
|
204
212
|
#
|
213
|
+
# @example Right click an element with shift key pressed
|
214
|
+
# browser.element(name: "new_user_button").right_click(:shift)
|
215
|
+
#
|
216
|
+
# @example Click an element with several modifier keys pressed
|
217
|
+
# browser.element(name: "new_user_button").right_click(:shift, :alt)
|
218
|
+
#
|
219
|
+
# @param [:shift, :alt, :control, :command, :meta] modifiers to press while right clicking.
|
220
|
+
#
|
221
|
+
|
222
|
+
def right_click(*modifiers)
|
223
|
+
element_call(:wait_for_present) do
|
224
|
+
action = driver.action
|
225
|
+
if modifiers.any?
|
226
|
+
modifiers.each { |mod| action.key_down mod }
|
227
|
+
action.context_click(@element)
|
228
|
+
modifiers.each { |mod| action.key_up mod }
|
229
|
+
action.perform
|
230
|
+
else
|
231
|
+
action.context_click(@element).perform
|
232
|
+
end
|
233
|
+
end
|
205
234
|
|
206
|
-
def right_click
|
207
|
-
element_call(:wait_for_present) { driver.action.context_click(@element).perform }
|
208
235
|
browser.after_hooks.run
|
209
236
|
end
|
210
237
|
|
@@ -247,7 +274,7 @@ module Watir
|
|
247
274
|
# Note that browser support may vary.
|
248
275
|
#
|
249
276
|
# @example
|
250
|
-
# browser.div(id: "draggable").drag_and_drop_by 100,
|
277
|
+
# browser.div(id: "draggable").drag_and_drop_by 100, 25
|
251
278
|
#
|
252
279
|
# @param [Integer] right_by
|
253
280
|
# @param [Integer] down_by
|
@@ -358,6 +385,8 @@ module Watir
|
|
358
385
|
#
|
359
386
|
|
360
387
|
def scroll_into_view
|
388
|
+
Watir.logger.deprecate 'Element#scroll_into_view', 'Element#scroll methods', ids: [:scroll_into_view]
|
389
|
+
|
361
390
|
element_call { @element.location_once_scrolled_into_view }
|
362
391
|
end
|
363
392
|
|
@@ -460,16 +489,7 @@ module Watir
|
|
460
489
|
msg = '#visible? behavior will be changing slightly, consider switching to #present? ' \
|
461
490
|
'(more details: http://watir.com/element-existentialism/)'
|
462
491
|
Watir.logger.warn msg, ids: [:visible_element]
|
463
|
-
|
464
|
-
if displayed.nil? && display_check
|
465
|
-
Watir.logger.deprecate 'Checking `#visible? == false` to determine a stale element',
|
466
|
-
'`#stale? == true`',
|
467
|
-
reference: 'http://watir.com/staleness-changes',
|
468
|
-
ids: [:stale_visible]
|
469
|
-
end
|
470
|
-
raise unknown_exception if displayed.nil?
|
471
|
-
|
472
|
-
displayed
|
492
|
+
display_check
|
473
493
|
end
|
474
494
|
|
475
495
|
#
|
@@ -492,14 +512,7 @@ module Watir
|
|
492
512
|
#
|
493
513
|
|
494
514
|
def present?
|
495
|
-
|
496
|
-
if displayed.nil? && display_check
|
497
|
-
Watir.logger.deprecate 'Checking `#present? == false` to determine a stale element',
|
498
|
-
'`#stale? == true`',
|
499
|
-
reference: 'http://watir.com/staleness-changes',
|
500
|
-
ids: [:stale_present]
|
501
|
-
end
|
502
|
-
displayed
|
515
|
+
display_check
|
503
516
|
rescue UnknownObjectException, UnknownFrameException
|
504
517
|
false
|
505
518
|
end
|
@@ -547,7 +560,7 @@ module Watir
|
|
547
560
|
#
|
548
561
|
|
549
562
|
def to_subtype
|
550
|
-
tag = tag_name
|
563
|
+
tag = tag_name
|
551
564
|
klass = if tag == 'input'
|
552
565
|
case attribute_value(:type)
|
553
566
|
when 'checkbox'
|
@@ -595,7 +608,7 @@ module Watir
|
|
595
608
|
def stale_in_context?
|
596
609
|
@element.css_value('staleness_check') # any wire call will check for staleness
|
597
610
|
false
|
598
|
-
rescue Selenium::WebDriver::Error::
|
611
|
+
rescue Selenium::WebDriver::Error::StaleElementReferenceError
|
599
612
|
true
|
600
613
|
end
|
601
614
|
|
@@ -686,7 +699,7 @@ module Watir
|
|
686
699
|
return assert_enabled unless Watir.relaxed_locate?
|
687
700
|
|
688
701
|
wait_for_exists
|
689
|
-
return unless [Input, Button, Select, Option].any? { |c| is_a? c } ||
|
702
|
+
return unless [Input, Button, Select, Option].any? { |c| is_a? c } || content_editable
|
690
703
|
return if enabled?
|
691
704
|
|
692
705
|
begin
|
@@ -698,9 +711,7 @@ module Watir
|
|
698
711
|
|
699
712
|
def wait_for_writable
|
700
713
|
wait_for_enabled
|
701
|
-
unless Watir.relaxed_locate?
|
702
|
-
raise_writable unless !respond_to?(:readonly?) || !readonly?
|
703
|
-
end
|
714
|
+
raise_writable unless Watir.relaxed_locate? || (!respond_to?(:readonly?) || !readonly?)
|
704
715
|
|
705
716
|
return if !respond_to?(:readonly?) || !readonly?
|
706
717
|
|
@@ -720,7 +731,11 @@ module Watir
|
|
720
731
|
end
|
721
732
|
|
722
733
|
def ensure_context
|
723
|
-
|
734
|
+
if @query_scope.is_a?(Browser) || !@query_scope.located? && @query_scope.is_a?(IFrame)
|
735
|
+
@query_scope.browser.locate
|
736
|
+
elsif @query_scope.located? && @query_scope.stale?
|
737
|
+
@query_scope.locate
|
738
|
+
end
|
724
739
|
@query_scope.switch_to! if @query_scope.is_a?(IFrame)
|
725
740
|
end
|
726
741
|
|
@@ -770,54 +785,52 @@ module Watir
|
|
770
785
|
@element.displayed?
|
771
786
|
rescue Selenium::WebDriver::Error::StaleElementReferenceError
|
772
787
|
reset!
|
773
|
-
|
788
|
+
retry
|
774
789
|
end
|
775
790
|
|
776
791
|
# TODO: - this will get addressed with Watir::Executor implementation
|
777
792
|
# rubocop:disable Metrics/AbcSize
|
778
793
|
# rubocop:disable Metrics/MethodLength
|
779
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
780
794
|
# rubocop:disable Metrics/CyclomaticComplexity:
|
795
|
+
# rubocop:disable Metrics/PerceivedComplexity::
|
781
796
|
def element_call(precondition = nil, &block)
|
782
797
|
caller = caller_locations(1, 1)[0].label
|
783
|
-
already_locked =
|
784
|
-
|
798
|
+
already_locked = browser.timer.locked?
|
799
|
+
browser.timer = Wait::Timer.new(timeout: Watir.default_timeout) unless already_locked
|
785
800
|
|
786
801
|
begin
|
787
802
|
check_condition(precondition, caller)
|
788
803
|
Watir.logger.debug "-> `Executing #{inspect}##{caller}`"
|
789
804
|
yield
|
790
|
-
rescue unknown_exception =>
|
805
|
+
rescue unknown_exception => e
|
791
806
|
element_call(:wait_for_exists, &block) if precondition.nil?
|
792
|
-
msg =
|
807
|
+
msg = e.message
|
793
808
|
msg += '; Maybe look in an iframe?' if @query_scope.iframe.exists?
|
794
|
-
custom_attributes = @locator.nil? ? [] : selector_builder.custom_attributes
|
809
|
+
custom_attributes = !defined?(@locator) || @locator.nil? ? [] : selector_builder.custom_attributes
|
795
810
|
unless custom_attributes.empty?
|
796
811
|
msg += "; Watir treated #{custom_attributes} as a non-HTML compliant attribute, ensure that was intended"
|
797
812
|
end
|
798
813
|
raise unknown_exception, msg
|
799
|
-
rescue Selenium::WebDriver::Error::StaleElementReferenceError
|
814
|
+
rescue Selenium::WebDriver::Error::StaleElementReferenceError, Selenium::WebDriver::Error::NoSuchElementError
|
800
815
|
reset!
|
801
816
|
retry
|
802
|
-
|
803
|
-
|
817
|
+
# TODO: - InvalidElementStateError is deprecated, so no longer calling `raise_disabled`
|
818
|
+
# need a better way to handle this
|
819
|
+
rescue Selenium::WebDriver::Error::ElementNotInteractableError
|
820
|
+
raise_present unless browser.timer.remaining_time.positive?
|
804
821
|
raise_present unless %i[wait_for_present wait_for_enabled wait_for_writable].include?(precondition)
|
805
822
|
retry
|
806
|
-
rescue Selenium::WebDriver::Error::InvalidElementStateError
|
807
|
-
raise_disabled unless Wait.timer.remaining_time.positive?
|
808
|
-
raise_disabled unless %i[wait_for_present wait_for_enabled wait_for_writable].include?(precondition)
|
809
|
-
retry
|
810
823
|
rescue Selenium::WebDriver::Error::NoSuchWindowError
|
811
824
|
raise NoMatchingWindowFoundException, 'browser window was closed'
|
812
825
|
ensure
|
813
826
|
Watir.logger.debug "<- `Completed #{inspect}##{caller}`"
|
814
|
-
|
827
|
+
browser.timer.reset! unless already_locked
|
815
828
|
end
|
816
829
|
end
|
817
830
|
# rubocop:enable Metrics/AbcSize
|
818
831
|
# rubocop:enable Metrics/MethodLength
|
832
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
819
833
|
# rubocop:enable Metrics/PerceivedComplexity
|
820
|
-
# rubocop:enable Metrics/CyclomaticComplexity:
|
821
834
|
|
822
835
|
def check_condition(condition, caller)
|
823
836
|
Watir.logger.debug "<- `Verifying precondition #{inspect}##{condition} for #{caller}`"
|