capybara 3.32.2 → 3.35.2
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.
- checksums.yaml +4 -4
- data/History.md +97 -15
- data/README.md +9 -4
- data/lib/capybara.rb +18 -8
- data/lib/capybara/config.rb +4 -6
- data/lib/capybara/cucumber.rb +1 -1
- data/lib/capybara/driver/base.rb +4 -0
- data/lib/capybara/helpers.rb +25 -1
- data/lib/capybara/minitest.rb +2 -3
- data/lib/capybara/minitest/spec.rb +14 -11
- data/lib/capybara/node/actions.rb +16 -21
- data/lib/capybara/node/base.rb +6 -6
- data/lib/capybara/node/element.rb +1 -5
- data/lib/capybara/node/finders.rb +7 -6
- data/lib/capybara/node/matchers.rb +12 -12
- data/lib/capybara/node/simple.rb +5 -1
- data/lib/capybara/queries/ancestor_query.rb +1 -1
- data/lib/capybara/queries/current_path_query.rb +14 -4
- data/lib/capybara/queries/selector_query.rb +40 -18
- data/lib/capybara/queries/sibling_query.rb +1 -1
- data/lib/capybara/queries/style_query.rb +1 -1
- data/lib/capybara/queries/text_query.rb +7 -1
- data/lib/capybara/rack_test/browser.rb +7 -3
- data/lib/capybara/rack_test/driver.rb +1 -0
- data/lib/capybara/rack_test/form.rb +1 -1
- data/lib/capybara/rack_test/node.rb +1 -1
- data/lib/capybara/registration_container.rb +44 -0
- data/lib/capybara/registrations/drivers.rb +18 -12
- data/lib/capybara/registrations/patches/puma_ssl.rb +3 -1
- data/lib/capybara/registrations/servers.rb +2 -1
- data/lib/capybara/result.rb +6 -10
- data/lib/capybara/rspec.rb +2 -0
- data/lib/capybara/rspec/matcher_proxies.rb +1 -1
- data/lib/capybara/rspec/matchers.rb +7 -6
- data/lib/capybara/rspec/matchers/have_current_path.rb +2 -2
- data/lib/capybara/rspec/matchers/match_style.rb +5 -0
- data/lib/capybara/selector.rb +12 -3
- data/lib/capybara/selector/builders/css_builder.rb +1 -1
- data/lib/capybara/selector/builders/xpath_builder.rb +3 -1
- data/lib/capybara/selector/definition.rb +11 -9
- data/lib/capybara/selector/definition/button.rb +26 -14
- data/lib/capybara/selector/definition/css.rb +1 -1
- data/lib/capybara/selector/definition/datalist_input.rb +1 -1
- data/lib/capybara/selector/definition/element.rb +2 -1
- data/lib/capybara/selector/definition/fillable_field.rb +1 -1
- data/lib/capybara/selector/definition/label.rb +1 -1
- data/lib/capybara/selector/definition/link.rb +8 -0
- data/lib/capybara/selector/definition/select.rb +1 -1
- data/lib/capybara/selector/definition/table.rb +1 -1
- data/lib/capybara/selector/definition/table_row.rb +2 -2
- data/lib/capybara/selector/filter_set.rb +2 -2
- data/lib/capybara/selector/selector.rb +9 -1
- data/lib/capybara/selenium/atoms/src/isDisplayed.js +1 -1
- data/lib/capybara/selenium/driver.rb +48 -4
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +9 -11
- data/lib/capybara/selenium/driver_specializations/edge_driver.rb +9 -11
- data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +1 -1
- data/lib/capybara/selenium/extensions/find.rb +4 -4
- data/lib/capybara/selenium/extensions/scroll.rb +8 -10
- data/lib/capybara/selenium/logger_suppressor.rb +8 -2
- data/lib/capybara/selenium/node.rb +9 -5
- data/lib/capybara/selenium/nodes/chrome_node.rb +23 -5
- data/lib/capybara/selenium/nodes/firefox_node.rb +7 -2
- data/lib/capybara/selenium/nodes/safari_node.rb +1 -1
- data/lib/capybara/selenium/patches/atoms.rb +4 -4
- data/lib/capybara/selenium/patches/logs.rb +7 -9
- data/lib/capybara/server/animation_disabler.rb +8 -3
- data/lib/capybara/server/middleware.rb +4 -2
- data/lib/capybara/session.rb +23 -14
- data/lib/capybara/session/config.rb +3 -1
- data/lib/capybara/session/matchers.rb +11 -11
- data/lib/capybara/spec/public/test.js +13 -1
- data/lib/capybara/spec/session/accept_alert_spec.rb +1 -1
- data/lib/capybara/spec/session/check_spec.rb +6 -0
- data/lib/capybara/spec/session/click_button_spec.rb +11 -0
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +9 -0
- data/lib/capybara/spec/session/current_url_spec.rb +11 -1
- data/lib/capybara/spec/session/has_button_spec.rb +51 -0
- data/lib/capybara/spec/session/has_css_spec.rb +2 -1
- data/lib/capybara/spec/session/has_current_path_spec.rb +15 -2
- data/lib/capybara/spec/session/has_field_spec.rb +16 -0
- data/lib/capybara/spec/session/has_select_spec.rb +4 -4
- data/lib/capybara/spec/session/has_selector_spec.rb +4 -4
- data/lib/capybara/spec/session/has_text_spec.rb +0 -11
- data/lib/capybara/spec/session/html_spec.rb +1 -1
- data/lib/capybara/spec/session/matches_style_spec.rb +2 -2
- data/lib/capybara/spec/session/node_spec.rb +29 -9
- data/lib/capybara/spec/session/refresh_spec.rb +2 -1
- data/lib/capybara/spec/session/save_page_spec.rb +4 -4
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +1 -1
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +1 -1
- data/lib/capybara/spec/session/window/window_spec.rb +1 -1
- data/lib/capybara/spec/session/window/windows_spec.rb +1 -1
- data/lib/capybara/spec/spec_helper.rb +12 -12
- data/lib/capybara/spec/test_app.rb +23 -21
- data/lib/capybara/spec/views/form.erb +25 -2
- data/lib/capybara/spec/views/with_animation.erb +8 -0
- data/lib/capybara/spec/views/with_dragula.erb +3 -1
- data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
- data/lib/capybara/spec/views/with_js.erb +3 -0
- data/lib/capybara/spec/views/with_sortable_js.erb +1 -1
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/window.rb +3 -7
- data/spec/basic_node_spec.rb +9 -8
- data/spec/capybara_spec.rb +1 -1
- data/spec/dsl_spec.rb +14 -1
- data/spec/fixtures/selenium_driver_rspec_success.rb +1 -1
- data/spec/minitest_spec.rb +3 -2
- data/spec/rack_test_spec.rb +16 -5
- data/spec/rspec/features_spec.rb +3 -1
- data/spec/rspec/scenarios_spec.rb +4 -0
- data/spec/rspec/shared_spec_matchers.rb +61 -49
- data/spec/rspec_spec.rb +4 -0
- data/spec/selector_spec.rb +17 -2
- data/spec/selenium_spec_chrome.rb +39 -20
- data/spec/selenium_spec_chrome_remote.rb +5 -1
- data/spec/selenium_spec_firefox.rb +15 -13
- data/spec/server_spec.rb +60 -49
- data/spec/shared_selenium_session.rb +83 -1
- data/spec/spec_helper.rb +1 -1
- metadata +49 -14
- data/lib/capybara/spec/session/source_spec.rb +0 -0
|
@@ -4,30 +4,30 @@ Capybara.add_selector(:button, locator_type: [String, Symbol]) do
|
|
|
4
4
|
xpath(:value, :title, :type, :name) do |locator, **options|
|
|
5
5
|
input_btn_xpath = XPath.descendant(:input)[XPath.attr(:type).one_of('submit', 'reset', 'image', 'button')]
|
|
6
6
|
btn_xpath = XPath.descendant(:button)
|
|
7
|
+
aria_btn_xpath = XPath.descendant[XPath.attr(:role).equals('button')]
|
|
7
8
|
image_btn_xpath = XPath.descendant(:input)[XPath.attr(:type) == 'image']
|
|
8
9
|
|
|
9
10
|
unless locator.nil?
|
|
10
11
|
locator = locator.to_s
|
|
11
|
-
locator_matchers =
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
locator_matchers |= XPath.attr(:'aria-label').is(locator) if enable_aria_label
|
|
16
|
-
locator_matchers |= XPath.attr(test_id) == locator if test_id
|
|
12
|
+
locator_matchers = combine_locators(locator, config: self)
|
|
13
|
+
btn_matchers = locator_matchers |
|
|
14
|
+
XPath.string.n.is(locator) |
|
|
15
|
+
XPath.descendant(:img)[XPath.attr(:alt).is(locator)]
|
|
17
16
|
|
|
18
|
-
input_btn_xpath = input_btn_xpath[locator_matchers]
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
XPath.string.n.is(locator) |
|
|
22
|
-
XPath.descendant(:img)[XPath.attr(:alt).is(locator)]]
|
|
17
|
+
input_btn_xpath = input_btn_xpath[locator_matchers] + locate_label(locator).descendant(input_btn_xpath)
|
|
18
|
+
btn_xpath = btn_xpath[btn_matchers] + locate_label(locator).descendant(btn_xpath)
|
|
19
|
+
aria_btn_xpath = aria_btn_xpath[btn_matchers]
|
|
23
20
|
|
|
24
21
|
alt_matches = XPath.attr(:alt).is(locator)
|
|
25
22
|
alt_matches |= XPath.attr(:'aria-label').is(locator) if enable_aria_label
|
|
26
|
-
image_btn_xpath = image_btn_xpath[alt_matches]
|
|
23
|
+
image_btn_xpath = image_btn_xpath[alt_matches] + locate_label(locator).descendant(image_btn_xpath)
|
|
27
24
|
end
|
|
28
25
|
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
btn_xpaths = [input_btn_xpath, btn_xpath, image_btn_xpath]
|
|
27
|
+
btn_xpaths << aria_btn_xpath if enable_aria_role
|
|
28
|
+
|
|
29
|
+
%i[value title type].inject(btn_xpaths.inject(&:union)) do |memo, ef|
|
|
30
|
+
memo.where(find_by_attr(ef, options[ef]))
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
|
|
@@ -48,4 +48,16 @@ Capybara.add_selector(:button, locator_type: [String, Symbol]) do
|
|
|
48
48
|
describe_node_filters do |disabled: nil, **|
|
|
49
49
|
' that is disabled' if disabled == true
|
|
50
50
|
end
|
|
51
|
+
|
|
52
|
+
def combine_locators(locator, config:)
|
|
53
|
+
[
|
|
54
|
+
XPath.attr(:id).equals(locator),
|
|
55
|
+
XPath.attr(:name).equals(locator),
|
|
56
|
+
XPath.attr(:value).is(locator),
|
|
57
|
+
XPath.attr(:title).is(locator),
|
|
58
|
+
(XPath.attr(:id) == XPath.anywhere(:label)[XPath.string.n.is(locator)].attr(:for)),
|
|
59
|
+
(XPath.attr(:'aria-label').is(locator) if config.enable_aria_label),
|
|
60
|
+
(XPath.attr(config.test_id) == locator if config.test_id)
|
|
61
|
+
].compact.inject(&:|)
|
|
62
|
+
end
|
|
51
63
|
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Capybara.add_selector(:css, locator_type: [String, Symbol], raw_locator: true) do
|
|
4
4
|
css do |css|
|
|
5
5
|
if css.is_a? Symbol
|
|
6
|
-
warn "DEPRECATED: Passing a symbol (#{css.inspect}) as the CSS locator is deprecated - please pass a string instead."
|
|
6
|
+
Capybara::Helpers.warn "DEPRECATED: Passing a symbol (#{css.inspect}) as the CSS locator is deprecated - please pass a string instead : #{Capybara::Helpers.filter_backtrace(caller)}"
|
|
7
7
|
end
|
|
8
8
|
css
|
|
9
9
|
end
|
|
@@ -19,7 +19,7 @@ Capybara.add_selector(:datalist_input, locator_type: [String, Symbol]) do
|
|
|
19
19
|
|
|
20
20
|
expression_filter(:with_options) do |expr, options|
|
|
21
21
|
options.inject(expr) do |xpath, option|
|
|
22
|
-
xpath
|
|
22
|
+
xpath.where(XPath.attr(:list) == XPath.anywhere(:datalist)[expression_for(:datalist_option, option)].attr(:id))
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
|
|
@@ -18,7 +18,8 @@ Capybara.add_selector(:element, locator_type: [String, Symbol]) do
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
describe_expression_filters do |**options|
|
|
21
|
-
|
|
21
|
+
boolean_values = [true, false]
|
|
22
|
+
booleans, values = options.partition { |_k, v| boolean_values.include? v }.map(&:to_h)
|
|
22
23
|
desc = describe_all_expression_filters(**values)
|
|
23
24
|
desc + booleans.map do |k, v|
|
|
24
25
|
v ? " with #{k} attribute" : "without #{k} attribute"
|
|
@@ -18,7 +18,7 @@ Capybara.add_selector(:fillable_field, locator_type: [String, Symbol]) do
|
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
filter_set(:_field, %i[disabled multiple name placeholder valid])
|
|
21
|
+
filter_set(:_field, %i[disabled multiple name placeholder valid validation_message])
|
|
22
22
|
|
|
23
23
|
node_filter(:with) do |node, with|
|
|
24
24
|
val = node.value
|
|
@@ -53,7 +53,7 @@ Capybara.add_selector(:label, locator_type: [String, Symbol]) do
|
|
|
53
53
|
end
|
|
54
54
|
end
|
|
55
55
|
describe_node_filters do |**options|
|
|
56
|
-
" for element #{options[:for]}" if options[:for]
|
|
56
|
+
" for element #{options[:for]}" if options[:for].is_a?(Capybara::Node::Element)
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
def labelable_elements
|
|
@@ -5,6 +5,13 @@ Capybara.add_selector(:link, locator_type: [String, Symbol]) do
|
|
|
5
5
|
xpath = XPath.descendant(:a)
|
|
6
6
|
xpath = builder(xpath).add_attribute_conditions(href: href) unless href == false
|
|
7
7
|
|
|
8
|
+
if enable_aria_role
|
|
9
|
+
role_path = XPath.descendant[XPath.attr(:role).equals('link')]
|
|
10
|
+
role_path = builder(role_path).add_attribute_conditions(href: href) unless [true, false].include? href
|
|
11
|
+
|
|
12
|
+
xpath += role_path
|
|
13
|
+
end
|
|
14
|
+
|
|
8
15
|
unless locator.nil?
|
|
9
16
|
locator = locator.to_s
|
|
10
17
|
matchers = [XPath.attr(:id) == locator,
|
|
@@ -18,6 +25,7 @@ Capybara.add_selector(:link, locator_type: [String, Symbol]) do
|
|
|
18
25
|
|
|
19
26
|
xpath = xpath[find_by_attr(:title, title)]
|
|
20
27
|
xpath = xpath[XPath.descendant(:img)[XPath.attr(:alt) == alt]] if alt
|
|
28
|
+
|
|
21
29
|
xpath
|
|
22
30
|
end
|
|
23
31
|
|
|
@@ -33,7 +33,7 @@ Capybara.add_selector(:select, locator_type: [String, Symbol]) do
|
|
|
33
33
|
|
|
34
34
|
expression_filter(:with_options) do |expr, options|
|
|
35
35
|
options.inject(expr) do |xpath, option|
|
|
36
|
-
xpath
|
|
36
|
+
xpath.where(expression_for(:option, option))
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
39
|
|
|
@@ -43,7 +43,7 @@ Capybara.add_selector(:table, locator_type: [String, Symbol]) do
|
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
expression_filter(:cols, valid_values: [Array]) do |xpath, cols|
|
|
46
|
-
raise ArgumentError, ':cols must be an Array of Arrays' unless cols.all?
|
|
46
|
+
raise ArgumentError, ':cols must be an Array of Arrays' unless cols.all?(Array)
|
|
47
47
|
|
|
48
48
|
rows = cols.transpose
|
|
49
49
|
col_conditions = rows.map { |row| match_row(row, match_size: true) }.reduce(:&)
|
|
@@ -9,12 +9,12 @@ Capybara.add_selector(:table_row, locator_type: [Array, Hash]) do
|
|
|
9
9
|
cell_xp = XPath.descendant(:td)[
|
|
10
10
|
XPath.string.n.is(cell) & XPath.position.equals(header_xp.preceding_sibling.count.plus(1))
|
|
11
11
|
]
|
|
12
|
-
xp
|
|
12
|
+
xp.where(cell_xp)
|
|
13
13
|
end
|
|
14
14
|
else
|
|
15
15
|
initial_td = XPath.descendant(:td)[XPath.string.n.is(locator.shift)]
|
|
16
16
|
tds = locator.reverse.map { |cell| XPath.following_sibling(:td)[XPath.string.n.is(cell)] }
|
|
17
|
-
.reduce { |xp, cell| xp
|
|
17
|
+
.reduce { |xp, cell| xp.where(cell) }
|
|
18
18
|
xpath[initial_td[tds]]
|
|
19
19
|
end
|
|
20
20
|
end
|
|
@@ -12,7 +12,7 @@ module Capybara
|
|
|
12
12
|
@node_filters = {}
|
|
13
13
|
@expression_filters = {}
|
|
14
14
|
@descriptions = Hash.new { |hsh, key| hsh[key] = [] }
|
|
15
|
-
instance_eval(&block)
|
|
15
|
+
instance_eval(&block) if block
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def node_filter(names, *types, **options, &block)
|
|
@@ -49,7 +49,7 @@ module Capybara
|
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
def descriptions
|
|
52
|
-
warn 'DEPRECATED: FilterSet#descriptions is deprecated without replacement'
|
|
52
|
+
Capybara::Helpers.warn 'DEPRECATED: FilterSet#descriptions is deprecated without replacement'
|
|
53
53
|
[undeclared_descriptions, node_filter_descriptions, expression_filter_descriptions].flatten
|
|
54
54
|
end
|
|
55
55
|
|
|
@@ -48,6 +48,10 @@ module Capybara
|
|
|
48
48
|
@config[:enable_aria_label]
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
+
def enable_aria_role
|
|
52
|
+
@config[:enable_aria_role]
|
|
53
|
+
end
|
|
54
|
+
|
|
51
55
|
def test_id
|
|
52
56
|
@config[:test_id]
|
|
53
57
|
end
|
|
@@ -128,7 +132,11 @@ module Capybara
|
|
|
128
132
|
attr_matchers |= XPath.attr(test_id) == locator if test_id
|
|
129
133
|
|
|
130
134
|
locate_xpath = locate_xpath[attr_matchers]
|
|
131
|
-
locate_xpath +
|
|
135
|
+
locate_xpath + locate_label(locator).descendant(xpath)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def locate_label(locator)
|
|
139
|
+
XPath.descendant(:label)[XPath.string.n.is(locator)]
|
|
132
140
|
end
|
|
133
141
|
|
|
134
142
|
def find_by_attr(attribute, value)
|
|
@@ -158,7 +158,7 @@
|
|
|
158
158
|
// the overflow style of the body, and the body is really overflow:visible.
|
|
159
159
|
var overflowElem = e;
|
|
160
160
|
if (htmlOverflowStyle == "visible") {
|
|
161
|
-
//
|
|
161
|
+
// NOTE: bodyElem will be null/undefined in SVG documents.
|
|
162
162
|
if (e == htmlElem && bodyElem) {
|
|
163
163
|
overflowElem = bodyElem;
|
|
164
164
|
} else if (e == bodyElem) {
|
|
@@ -12,20 +12,44 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
12
12
|
clear_session_storage: nil
|
|
13
13
|
}.freeze
|
|
14
14
|
SPECIAL_OPTIONS = %i[browser clear_local_storage clear_session_storage timeout native_displayed].freeze
|
|
15
|
+
CAPS_VERSION = Gem::Requirement.new('~> 4.0.0.alpha6')
|
|
16
|
+
|
|
15
17
|
attr_reader :app, :options
|
|
16
18
|
|
|
17
19
|
class << self
|
|
20
|
+
attr_reader :selenium_webdriver_version
|
|
21
|
+
|
|
18
22
|
def load_selenium
|
|
19
23
|
require 'selenium-webdriver'
|
|
20
24
|
require 'capybara/selenium/logger_suppressor'
|
|
21
25
|
require 'capybara/selenium/patches/atoms'
|
|
22
26
|
require 'capybara/selenium/patches/is_displayed'
|
|
23
27
|
require 'capybara/selenium/patches/action_pauser'
|
|
24
|
-
|
|
28
|
+
|
|
29
|
+
# Look up the version of `selenium-webdriver` to
|
|
30
|
+
# see if it's a version we support.
|
|
31
|
+
#
|
|
32
|
+
# By default, we use Gem.loaded_specs to determine
|
|
33
|
+
# the version number. However, in some cases, such
|
|
34
|
+
# as when loading `selenium-webdriver` outside of
|
|
35
|
+
# Rubygems, we fall back to referencing
|
|
36
|
+
# Selenium::WebDriver::VERSION. Ideally we'd
|
|
37
|
+
# use the constant in all cases, but earlier versions
|
|
38
|
+
# of `selenium-webdriver` didn't provide the constant.
|
|
39
|
+
@selenium_webdriver_version =
|
|
40
|
+
if Gem.loaded_specs['selenium-webdriver']
|
|
41
|
+
Gem.loaded_specs['selenium-webdriver'].version
|
|
42
|
+
else
|
|
43
|
+
Gem::Version.new(Selenium::WebDriver::VERSION)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
unless Gem::Requirement.new('>= 3.5.0').satisfied_by? @selenium_webdriver_version
|
|
25
47
|
warn "Warning: You're using an unsupported version of selenium-webdriver, please upgrade."
|
|
26
48
|
end
|
|
49
|
+
|
|
50
|
+
@selenium_webdriver_version
|
|
27
51
|
rescue LoadError => e
|
|
28
|
-
raise e unless e.message.
|
|
52
|
+
raise e unless e.message.include?('selenium-webdriver')
|
|
29
53
|
|
|
30
54
|
raise LoadError, "Capybara's selenium driver is unable to load `selenium-webdriver`, please install the gem and add `gem 'selenium-webdriver'` to your Gemfile if you are using bundler."
|
|
31
55
|
end
|
|
@@ -49,7 +73,15 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
49
73
|
end
|
|
50
74
|
end
|
|
51
75
|
processed_options = options.reject { |key, _val| SPECIAL_OPTIONS.include?(key) }
|
|
52
|
-
|
|
76
|
+
|
|
77
|
+
@browser = if options[:browser] == :firefox &&
|
|
78
|
+
RUBY_VERSION >= '3.0' &&
|
|
79
|
+
Capybara::Selenium::Driver.selenium_webdriver_version <= Gem::Version.new('4.0.0.alpha1')
|
|
80
|
+
# selenium-webdriver 3.x doesn't correctly pass options through for Firefox with Ruby 3 so workaround that
|
|
81
|
+
Selenium::WebDriver::Firefox::Driver.new(**processed_options)
|
|
82
|
+
else
|
|
83
|
+
Selenium::WebDriver.for(options[:browser], processed_options)
|
|
84
|
+
end
|
|
53
85
|
|
|
54
86
|
specialize_driver
|
|
55
87
|
setup_exit_handler
|
|
@@ -58,6 +90,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
58
90
|
end
|
|
59
91
|
|
|
60
92
|
def initialize(app, **options)
|
|
93
|
+
super()
|
|
61
94
|
self.class.load_selenium
|
|
62
95
|
@app = app
|
|
63
96
|
@browser = nil
|
|
@@ -85,6 +118,8 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
85
118
|
|
|
86
119
|
def html
|
|
87
120
|
browser.page_source
|
|
121
|
+
rescue Selenium::WebDriver::Error::JavascriptError => e
|
|
122
|
+
raise unless e.message.include?('documentElement is null')
|
|
88
123
|
end
|
|
89
124
|
|
|
90
125
|
def title
|
|
@@ -113,6 +148,10 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
113
148
|
unwrap_script_result(result)
|
|
114
149
|
end
|
|
115
150
|
|
|
151
|
+
def send_keys(*args)
|
|
152
|
+
active_element.send_keys(*args)
|
|
153
|
+
end
|
|
154
|
+
|
|
116
155
|
def save_screenshot(path, **_options)
|
|
117
156
|
browser.save_screenshot(path)
|
|
118
157
|
end
|
|
@@ -241,7 +280,8 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
241
280
|
|
|
242
281
|
def quit
|
|
243
282
|
@browser&.quit
|
|
244
|
-
rescue Selenium::WebDriver::Error::SessionNotCreatedError, Errno::ECONNREFUSED
|
|
283
|
+
rescue Selenium::WebDriver::Error::SessionNotCreatedError, Errno::ECONNREFUSED,
|
|
284
|
+
Selenium::WebDriver::Error::InvalidSessionIdError
|
|
245
285
|
# Browser must have already gone
|
|
246
286
|
rescue Selenium::WebDriver::Error::UnknownError => e
|
|
247
287
|
unless silenced_unknown_error_message?(e.message) # Most likely already gone
|
|
@@ -435,6 +475,10 @@ private
|
|
|
435
475
|
browser
|
|
436
476
|
end
|
|
437
477
|
|
|
478
|
+
def active_element
|
|
479
|
+
browser.switch_to.active_element
|
|
480
|
+
end
|
|
481
|
+
|
|
438
482
|
def build_node(native_node, initial_cache = {})
|
|
439
483
|
::Capybara::Selenium::Node.new(self, native_node, initial_cache)
|
|
440
484
|
end
|
|
@@ -7,27 +7,25 @@ module Capybara::Selenium::Driver::ChromeDriver
|
|
|
7
7
|
def self.extended(base)
|
|
8
8
|
bridge = base.send(:bridge)
|
|
9
9
|
bridge.extend Capybara::Selenium::ChromeLogs unless bridge.respond_to?(:log)
|
|
10
|
-
bridge.extend Capybara::Selenium::IsDisplayed unless bridge.
|
|
10
|
+
bridge.extend Capybara::Selenium::IsDisplayed unless bridge.send(:commands, :is_element_displayed)
|
|
11
11
|
base.options[:native_displayed] = false if base.options[:native_displayed].nil?
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def fullscreen_window(handle)
|
|
15
15
|
within_given_window(handle) do
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
result['value']
|
|
23
|
-
end
|
|
16
|
+
super
|
|
17
|
+
rescue NoMethodError => e
|
|
18
|
+
raise unless e.message.include?('full_screen_window')
|
|
19
|
+
|
|
20
|
+
result = bridge.http.call(:post, "session/#{bridge.session_id}/window/fullscreen", {})
|
|
21
|
+
result['value']
|
|
24
22
|
end
|
|
25
23
|
end
|
|
26
24
|
|
|
27
25
|
def resize_window_to(handle, width, height)
|
|
28
26
|
super
|
|
29
27
|
rescue Selenium::WebDriver::Error::UnknownError => e
|
|
30
|
-
raise unless e.message.
|
|
28
|
+
raise unless e.message.include?('failed to change window state')
|
|
31
29
|
|
|
32
30
|
# Chromedriver doesn't wait long enough for state to change when coming out of fullscreen
|
|
33
31
|
# and raises unnecessary error. Wait a bit and try again.
|
|
@@ -65,7 +63,7 @@ private
|
|
|
65
63
|
end
|
|
66
64
|
|
|
67
65
|
def clear_all_storage?
|
|
68
|
-
storage_clears.none?
|
|
66
|
+
storage_clears.none? false
|
|
69
67
|
end
|
|
70
68
|
|
|
71
69
|
def uniform_storage_clear?
|
|
@@ -5,7 +5,7 @@ require 'capybara/selenium/nodes/edge_node'
|
|
|
5
5
|
module Capybara::Selenium::Driver::EdgeDriver
|
|
6
6
|
def self.extended(base)
|
|
7
7
|
bridge = base.send(:bridge)
|
|
8
|
-
bridge.extend Capybara::Selenium::IsDisplayed unless bridge.
|
|
8
|
+
bridge.extend Capybara::Selenium::IsDisplayed unless bridge.send(:commands, :is_element_displayed)
|
|
9
9
|
base.options[:native_displayed] = false if base.options[:native_displayed].nil?
|
|
10
10
|
end
|
|
11
11
|
|
|
@@ -13,21 +13,19 @@ module Capybara::Selenium::Driver::EdgeDriver
|
|
|
13
13
|
return super if edgedriver_version < 75
|
|
14
14
|
|
|
15
15
|
within_given_window(handle) do
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
result['value']
|
|
23
|
-
end
|
|
16
|
+
super
|
|
17
|
+
rescue NoMethodError => e
|
|
18
|
+
raise unless e.message.include?('full_screen_window')
|
|
19
|
+
|
|
20
|
+
result = bridge.http.call(:post, "session/#{bridge.session_id}/window/fullscreen", {})
|
|
21
|
+
result['value']
|
|
24
22
|
end
|
|
25
23
|
end
|
|
26
24
|
|
|
27
25
|
def resize_window_to(handle, width, height)
|
|
28
26
|
super
|
|
29
27
|
rescue Selenium::WebDriver::Error::UnknownError => e
|
|
30
|
-
raise unless e.message.
|
|
28
|
+
raise unless e.message.include?('failed to change window state')
|
|
31
29
|
|
|
32
30
|
# Chromedriver doesn't wait long enough for state to change when coming out of fullscreen
|
|
33
31
|
# and raises unnecessary error. Wait a bit and try again.
|
|
@@ -74,7 +72,7 @@ private
|
|
|
74
72
|
end
|
|
75
73
|
|
|
76
74
|
def clear_all_storage?
|
|
77
|
-
storage_clears.none?
|
|
75
|
+
storage_clears.none? false
|
|
78
76
|
end
|
|
79
77
|
|
|
80
78
|
def uniform_storage_clear?
|
|
@@ -6,7 +6,7 @@ module Capybara::Selenium::Driver::FirefoxDriver
|
|
|
6
6
|
def self.extended(driver)
|
|
7
7
|
driver.extend Capybara::Selenium::Driver::W3CFirefoxDriver if w3c?(driver)
|
|
8
8
|
bridge = driver.send(:bridge)
|
|
9
|
-
bridge.extend Capybara::Selenium::IsDisplayed unless bridge.
|
|
9
|
+
bridge.extend Capybara::Selenium::IsDisplayed unless bridge.send(:commands, :is_element_displayed)
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def self.w3c?(driver)
|
|
@@ -28,7 +28,7 @@ module Capybara
|
|
|
28
28
|
hints_js, functions = build_hints_js(uses_visibility, styles, position)
|
|
29
29
|
return [] unless functions.any?
|
|
30
30
|
|
|
31
|
-
es_context.execute_script(hints_js, elements).map! do |results|
|
|
31
|
+
(es_context.execute_script(hints_js, elements) || []).map! do |results|
|
|
32
32
|
hint = {}
|
|
33
33
|
hint[:style] = results.pop if functions.include?(:style_func)
|
|
34
34
|
hint[:position] = results.pop if functions.include?(:position_func)
|
|
@@ -100,9 +100,9 @@ module Capybara
|
|
|
100
100
|
def is_displayed_atom # rubocop:disable Naming/PredicateName
|
|
101
101
|
@@is_displayed_atom ||= begin # rubocop:disable Style/ClassVars
|
|
102
102
|
browser.send(:bridge).send(:read_atom, 'isDisplayed')
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
rescue StandardError
|
|
104
|
+
# If the atom doesn't exist or other error
|
|
105
|
+
''
|
|
106
106
|
end
|
|
107
107
|
end
|
|
108
108
|
end
|