capybara 3.30.0 → 3.35.3
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 +153 -13
- 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/dsl.rb +10 -2
- data/lib/capybara/helpers.rb +25 -1
- data/lib/capybara/minitest.rb +232 -144
- data/lib/capybara/minitest/spec.rb +156 -97
- data/lib/capybara/node/actions.rb +16 -21
- data/lib/capybara/node/base.rb +6 -6
- data/lib/capybara/node/element.rb +14 -13
- data/lib/capybara/node/finders.rb +12 -7
- data/lib/capybara/node/matchers.rb +36 -27
- data/lib/capybara/node/simple.rb +6 -2
- data/lib/capybara/queries/ancestor_query.rb +1 -1
- data/lib/capybara/queries/base_query.rb +2 -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 +9 -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 +35 -10
- 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 +3 -2
- data/lib/capybara/result.rb +35 -15
- data/lib/capybara/rspec.rb +2 -0
- data/lib/capybara/rspec/matcher_proxies.rb +5 -5
- data/lib/capybara/rspec/matchers.rb +33 -32
- data/lib/capybara/rspec/matchers/base.rb +12 -6
- data/lib/capybara/rspec/matchers/count_sugar.rb +2 -1
- data/lib/capybara/rspec/matchers/have_ancestor.rb +4 -3
- data/lib/capybara/rspec/matchers/have_current_path.rb +2 -2
- data/lib/capybara/rspec/matchers/have_selector.rb +15 -7
- data/lib/capybara/rspec/matchers/have_sibling.rb +3 -3
- data/lib/capybara/rspec/matchers/have_text.rb +3 -3
- data/lib/capybara/rspec/matchers/have_title.rb +2 -2
- data/lib/capybara/rspec/matchers/match_selector.rb +3 -3
- data/lib/capybara/rspec/matchers/match_style.rb +7 -2
- data/lib/capybara/rspec/matchers/spatial_sugar.rb +2 -1
- data/lib/capybara/selector.rb +14 -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 +2 -2
- data/lib/capybara/selector/definition/link.rb +8 -0
- data/lib/capybara/selector/definition/select.rb +32 -13
- 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/getAttribute.min.js +1 -1
- data/lib/capybara/selenium/atoms/src/getAttribute.js +1 -1
- data/lib/capybara/selenium/atoms/src/isDisplayed.js +1 -1
- data/lib/capybara/selenium/driver.rb +52 -7
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +10 -12
- data/lib/capybara/selenium/driver_specializations/edge_driver.rb +9 -11
- data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +3 -3
- data/lib/capybara/selenium/extensions/find.rb +4 -4
- data/lib/capybara/selenium/extensions/html5_drag.rb +24 -8
- data/lib/capybara/selenium/extensions/scroll.rb +8 -10
- data/lib/capybara/selenium/logger_suppressor.rb +8 -2
- data/lib/capybara/selenium/node.rb +96 -16
- data/lib/capybara/selenium/nodes/chrome_node.rb +27 -16
- data/lib/capybara/selenium/nodes/edge_node.rb +1 -1
- data/lib/capybara/selenium/nodes/firefox_node.rb +9 -4
- data/lib/capybara/selenium/nodes/safari_node.rb +1 -1
- data/lib/capybara/selenium/patches/action_pauser.rb +26 -0
- 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 +53 -29
- data/lib/capybara/session/config.rb +3 -1
- data/lib/capybara/session/matchers.rb +11 -11
- data/lib/capybara/spec/public/test.js +64 -7
- data/lib/capybara/spec/session/accept_alert_spec.rb +1 -1
- data/lib/capybara/spec/session/all_spec.rb +45 -5
- data/lib/capybara/spec/session/assert_text_spec.rb +5 -5
- 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/fill_in_spec.rb +29 -0
- data/lib/capybara/spec/session/find_spec.rb +11 -8
- data/lib/capybara/spec/session/has_button_spec.rb +51 -0
- data/lib/capybara/spec/session/has_css_spec.rb +14 -10
- 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 +32 -4
- data/lib/capybara/spec/session/has_selector_spec.rb +4 -4
- data/lib/capybara/spec/session/has_text_spec.rb +5 -12
- 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 +169 -33
- 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 +8 -8
- data/lib/capybara/spec/session/window/windows_spec.rb +1 -1
- data/lib/capybara/spec/spec_helper.rb +13 -14
- data/lib/capybara/spec/test_app.rb +23 -21
- data/lib/capybara/spec/views/form.erb +36 -3
- 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_html.erb +2 -2
- 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 +28 -6
- data/spec/regexp_dissassembler_spec.rb +0 -4
- data/spec/result_spec.rb +40 -29
- data/spec/rspec/features_spec.rb +3 -1
- data/spec/rspec/scenarios_spec.rb +4 -0
- data/spec/rspec/shared_spec_matchers.rb +63 -51
- data/spec/rspec_spec.rb +4 -0
- data/spec/selector_spec.rb +17 -2
- data/spec/selenium_spec_chrome.rb +45 -21
- data/spec/selenium_spec_chrome_remote.rb +7 -1
- data/spec/selenium_spec_firefox.rb +15 -13
- data/spec/server_spec.rb +60 -49
- data/spec/shared_selenium_node.rb +18 -0
- data/spec/shared_selenium_session.rb +98 -7
- data/spec/spec_helper.rb +1 -1
- metadata +50 -14
- data/lib/capybara/spec/session/source_spec.rb +0 -0
|
@@ -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
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Capybara.add_selector(:label, locator_type: [String, Symbol]) do
|
|
4
4
|
label 'label'
|
|
5
|
-
xpath(:for) do |locator, options|
|
|
5
|
+
xpath(:for) do |locator, **options|
|
|
6
6
|
xpath = XPath.descendant(:label)
|
|
7
7
|
unless locator.nil?
|
|
8
8
|
locator_matchers = XPath.string.n.is(locator.to_s) | (XPath.attr(:id) == locator.to_s)
|
|
@@ -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
|
|
|
@@ -11,35 +11,41 @@ Capybara.add_selector(:select, locator_type: [String, Symbol]) do
|
|
|
11
11
|
filter_set(:_field, %i[disabled multiple name placeholder])
|
|
12
12
|
|
|
13
13
|
node_filter(:options) do |node, options|
|
|
14
|
-
actual =
|
|
15
|
-
node.all(:xpath, './/option', wait: false).map(&:text)
|
|
16
|
-
else
|
|
17
|
-
node.all(:xpath, './/option', visible: false, wait: false).map { |option| option.text(:all) }
|
|
18
|
-
end
|
|
14
|
+
actual = options_text(node)
|
|
19
15
|
(options.sort == actual.sort).tap do |res|
|
|
20
16
|
add_error("Expected options #{options.inspect} found #{actual.inspect}") unless res
|
|
21
17
|
end
|
|
22
18
|
end
|
|
23
19
|
|
|
20
|
+
node_filter(:enabled_options) do |node, options|
|
|
21
|
+
actual = options_text(node) { |o| !o.disabled? }
|
|
22
|
+
(options.sort == actual.sort).tap do |res|
|
|
23
|
+
add_error("Expected enabled options #{options.inspect} found #{actual.inspect}") unless res
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
node_filter(:disabled_options) do |node, options|
|
|
28
|
+
actual = options_text(node, &:disabled?)
|
|
29
|
+
(options.sort == actual.sort).tap do |res|
|
|
30
|
+
add_error("Expected disabled options #{options.inspect} found #{actual.inspect}") unless res
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
24
34
|
expression_filter(:with_options) do |expr, options|
|
|
25
35
|
options.inject(expr) do |xpath, option|
|
|
26
|
-
xpath
|
|
36
|
+
xpath.where(expression_for(:option, option))
|
|
27
37
|
end
|
|
28
38
|
end
|
|
29
39
|
|
|
30
40
|
node_filter(:selected) do |node, selected|
|
|
31
|
-
actual = node
|
|
32
|
-
.select(&:selected?)
|
|
33
|
-
.map { |option| option.text(:all) }
|
|
41
|
+
actual = options_text(node, visible: false, &:selected?)
|
|
34
42
|
(Array(selected).sort == actual.sort).tap do |res|
|
|
35
43
|
add_error("Expected #{selected.inspect} to be selected found #{actual.inspect}") unless res
|
|
36
44
|
end
|
|
37
45
|
end
|
|
38
46
|
|
|
39
47
|
node_filter(:with_selected) do |node, selected|
|
|
40
|
-
actual = node
|
|
41
|
-
.select(&:selected?)
|
|
42
|
-
.map { |option| option.text(:all) }
|
|
48
|
+
actual = options_text(node, visible: false, &:selected?)
|
|
43
49
|
(Array(selected) - actual).empty?.tap do |res|
|
|
44
50
|
add_error("Expected at least #{selected.inspect} to be selected found #{actual.inspect}") unless res
|
|
45
51
|
end
|
|
@@ -51,12 +57,25 @@ Capybara.add_selector(:select, locator_type: [String, Symbol]) do
|
|
|
51
57
|
desc
|
|
52
58
|
end
|
|
53
59
|
|
|
54
|
-
describe_node_filters do |
|
|
60
|
+
describe_node_filters do |
|
|
61
|
+
options: nil, disabled_options: nil, enabled_options: nil,
|
|
62
|
+
selected: nil, with_selected: nil,
|
|
63
|
+
disabled: nil, **|
|
|
55
64
|
desc = +''
|
|
56
65
|
desc << " with options #{options.inspect}" if options
|
|
66
|
+
desc << " with disabled options #{disabled_options.inspect}}" if disabled_options
|
|
67
|
+
desc << " with enabled options #{enabled_options.inspect}" if enabled_options
|
|
57
68
|
desc << " with #{selected.inspect} selected" if selected
|
|
58
69
|
desc << " with at least #{with_selected.inspect} selected" if with_selected
|
|
59
70
|
desc << ' which is disabled' if disabled
|
|
60
71
|
desc
|
|
61
72
|
end
|
|
73
|
+
|
|
74
|
+
def options_text(node, **opts, &filter_block)
|
|
75
|
+
opts[:wait] = false
|
|
76
|
+
opts[:visible] = false unless node.visible?
|
|
77
|
+
node.all(:xpath, './/option', **opts, &filter_block).map do |o|
|
|
78
|
+
o.text((:all if opts[:visible] == false))
|
|
79
|
+
end
|
|
80
|
+
end
|
|
62
81
|
end
|
|
@@ -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)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(){function u(e){var t=e.tagName.toUpperCase();if("OPTION"==t)return!0;if("INPUT"!=t)return!1;var r=e.type.toLowerCase();return"checkbox"==r||"radio"==r}function s(e){var t="selected",r=e.type&&e.type.toLowerCase();return"checkbox"!=r&&"radio"!=r||(t="checked"),!!e[t]}function c(e,t){var r=e.getAttributeNode(t);return r&&r.specified?r.value:null}var i=["allowfullscreen","allowpaymentrequest","allowusermedia","async","autofocus","autoplay","checked","compact","complete","controls","declare","default","defaultchecked","defaultselected","defer","disabled","ended","formnovalidate","hidden","indeterminate","iscontenteditable","ismap","itemscope","loop","multiple","muted","nohref","nomodule","noresize","noshade","novalidate","nowrap","open","paused","playsinline","pubdate","readonly","required","reversed","scoped","seamless","seeking","selected","truespeed","typemustmatch","willvalidate"],d={"class":"className",readonly:"readOnly"};return function f(e,t){var r=null,a=t.toLowerCase();if("style"==a)return(r=e.style)&&"string"!=typeof r&&(r=r.cssText),r;if(("selected"==a||"checked"==a)&&u(e))return s(e)?"true":null;if(tagName=e.tagName.toUpperCase(),"IMG"==tagName&&"src"==a||"A"==tagName&&"href"==a)return(r=c(e,a))&&(r=e[a]),r;if("spellcheck"==a){if(null
|
|
1
|
+
(function(){function u(e){var t=e.tagName.toUpperCase();if("OPTION"==t)return!0;if("INPUT"!=t)return!1;var r=e.type.toLowerCase();return"checkbox"==r||"radio"==r}function s(e){var t="selected",r=e.type&&e.type.toLowerCase();return"checkbox"!=r&&"radio"!=r||(t="checked"),!!e[t]}function c(e,t){var r=e.getAttributeNode(t);return r&&r.specified?r.value:null}var i=["allowfullscreen","allowpaymentrequest","allowusermedia","async","autofocus","autoplay","checked","compact","complete","controls","declare","default","defaultchecked","defaultselected","defer","disabled","ended","formnovalidate","hidden","indeterminate","iscontenteditable","ismap","itemscope","loop","multiple","muted","nohref","nomodule","noresize","noshade","novalidate","nowrap","open","paused","playsinline","pubdate","readonly","required","reversed","scoped","seamless","seeking","selected","truespeed","typemustmatch","willvalidate"],d={"class":"className",readonly:"readOnly"};return function f(e,t){var r=null,a=t.toLowerCase();if("style"==a)return(r=e.style)&&"string"!=typeof r&&(r=r.cssText),r;if(("selected"==a||"checked"==a)&&u(e))return s(e)?"true":null;if(tagName=e.tagName.toUpperCase(),"IMG"==tagName&&"src"==a||"A"==tagName&&"href"==a)return(r=c(e,a))&&(r=e[a]),r;if("spellcheck"==a){if(null!==(r=c(e,a))){if("false"==r.toLowerCase())return"false";if("true"==r.toLowerCase())return"true"}return e[a]+""}var l,n=d[t]||t;if(i.some(function(e){e==a}))return(r=!(null===(r=c(e,a)))||e[n])?"true":null;try{l=e[n]}catch(o){}return null!=(r=null==l||"object"==typeof l||"function"==typeof l?c(e,t):l)?r.toString():null}})()
|
|
@@ -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,19 +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'
|
|
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
|
|
24
47
|
warn "Warning: You're using an unsupported version of selenium-webdriver, please upgrade."
|
|
25
48
|
end
|
|
49
|
+
|
|
50
|
+
@selenium_webdriver_version
|
|
26
51
|
rescue LoadError => e
|
|
27
|
-
raise e unless e.message.
|
|
52
|
+
raise e unless e.message.include?('selenium-webdriver')
|
|
28
53
|
|
|
29
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."
|
|
30
55
|
end
|
|
@@ -48,7 +73,15 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
48
73
|
end
|
|
49
74
|
end
|
|
50
75
|
processed_options = options.reject { |key, _val| SPECIAL_OPTIONS.include?(key) }
|
|
51
|
-
|
|
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
|
|
52
85
|
|
|
53
86
|
specialize_driver
|
|
54
87
|
setup_exit_handler
|
|
@@ -57,6 +90,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
57
90
|
end
|
|
58
91
|
|
|
59
92
|
def initialize(app, **options)
|
|
93
|
+
super()
|
|
60
94
|
self.class.load_selenium
|
|
61
95
|
@app = app
|
|
62
96
|
@browser = nil
|
|
@@ -84,6 +118,8 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
84
118
|
|
|
85
119
|
def html
|
|
86
120
|
browser.page_source
|
|
121
|
+
rescue Selenium::WebDriver::Error::JavascriptError => e
|
|
122
|
+
raise unless e.message.include?('documentElement is null')
|
|
87
123
|
end
|
|
88
124
|
|
|
89
125
|
def title
|
|
@@ -112,6 +148,10 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
112
148
|
unwrap_script_result(result)
|
|
113
149
|
end
|
|
114
150
|
|
|
151
|
+
def send_keys(*args)
|
|
152
|
+
active_element.send_keys(*args)
|
|
153
|
+
end
|
|
154
|
+
|
|
115
155
|
def save_screenshot(path, **_options)
|
|
116
156
|
browser.save_screenshot(path)
|
|
117
157
|
end
|
|
@@ -240,7 +280,8 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
240
280
|
|
|
241
281
|
def quit
|
|
242
282
|
@browser&.quit
|
|
243
|
-
rescue Selenium::WebDriver::Error::SessionNotCreatedError, Errno::ECONNREFUSED
|
|
283
|
+
rescue Selenium::WebDriver::Error::SessionNotCreatedError, Errno::ECONNREFUSED,
|
|
284
|
+
Selenium::WebDriver::Error::InvalidSessionIdError
|
|
244
285
|
# Browser must have already gone
|
|
245
286
|
rescue Selenium::WebDriver::Error::UnknownError => e
|
|
246
287
|
unless silenced_unknown_error_message?(e.message) # Most likely already gone
|
|
@@ -292,7 +333,7 @@ private
|
|
|
292
333
|
def clear_browser_state
|
|
293
334
|
delete_all_cookies
|
|
294
335
|
clear_storage
|
|
295
|
-
rescue *clear_browser_state_errors
|
|
336
|
+
rescue *clear_browser_state_errors
|
|
296
337
|
# delete_all_cookies fails when we've previously gone
|
|
297
338
|
# to about:blank, so we rescue this error and do nothing
|
|
298
339
|
# instead.
|
|
@@ -316,7 +357,7 @@ private
|
|
|
316
357
|
def clear_storage
|
|
317
358
|
clear_session_storage unless options[:clear_session_storage] == false
|
|
318
359
|
clear_local_storage unless options[:clear_local_storage] == false
|
|
319
|
-
rescue Selenium::WebDriver::Error::JavascriptError
|
|
360
|
+
rescue Selenium::WebDriver::Error::JavascriptError
|
|
320
361
|
# session/local storage may not be available if on non-http pages (e.g. about:blank)
|
|
321
362
|
end
|
|
322
363
|
|
|
@@ -352,7 +393,7 @@ private
|
|
|
352
393
|
@browser.navigate.to(url)
|
|
353
394
|
sleep 0.1 # slight wait for alert
|
|
354
395
|
@browser.switch_to.alert.accept
|
|
355
|
-
rescue modal_error
|
|
396
|
+
rescue modal_error
|
|
356
397
|
# alert now gone, should mean navigation happened
|
|
357
398
|
end
|
|
358
399
|
|
|
@@ -434,6 +475,10 @@ private
|
|
|
434
475
|
browser
|
|
435
476
|
end
|
|
436
477
|
|
|
478
|
+
def active_element
|
|
479
|
+
browser.switch_to.active_element
|
|
480
|
+
end
|
|
481
|
+
|
|
437
482
|
def build_node(native_node, initial_cache = {})
|
|
438
483
|
::Capybara::Selenium::Node.new(self, native_node, initial_cache)
|
|
439
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?
|
|
@@ -96,7 +94,7 @@ private
|
|
|
96
94
|
|
|
97
95
|
def execute_cdp(cmd, params = {})
|
|
98
96
|
if browser.respond_to? :execute_cdp
|
|
99
|
-
browser.execute_cdp(cmd, params)
|
|
97
|
+
browser.execute_cdp(cmd, **params)
|
|
100
98
|
else
|
|
101
99
|
args = { cmd: cmd, params: params }
|
|
102
100
|
result = bridge.http.call(:post, "session/#{bridge.session_id}/goog/cdp/execute", args)
|