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
|
@@ -12,22 +12,28 @@ module Capybara
|
|
|
12
12
|
|
|
13
13
|
attr_reader :failure_message, :failure_message_when_negated
|
|
14
14
|
|
|
15
|
-
def initialize(*args, &filter_block)
|
|
15
|
+
def initialize(*args, **kw_args, &filter_block)
|
|
16
16
|
@args = args.dup
|
|
17
|
+
@kw_args = kw_args || {}
|
|
17
18
|
@filter_block = filter_block
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
private
|
|
21
22
|
|
|
22
23
|
def session_query_args
|
|
23
|
-
if @args.last.is_a? Hash
|
|
24
|
-
|
|
25
|
-
else
|
|
26
|
-
|
|
27
|
-
end
|
|
24
|
+
# if @args.last.is_a? Hash
|
|
25
|
+
# @args.last[:session_options] = session_options
|
|
26
|
+
# else
|
|
27
|
+
# @args.push(session_options: session_options)
|
|
28
|
+
# end
|
|
28
29
|
@args
|
|
29
30
|
end
|
|
30
31
|
|
|
32
|
+
def session_query_options
|
|
33
|
+
@kw_args[:session_options] = session_options
|
|
34
|
+
@kw_args
|
|
35
|
+
end
|
|
36
|
+
|
|
31
37
|
def session_options
|
|
32
38
|
@context_el ||= nil
|
|
33
39
|
if @context_el.respond_to? :session_options
|
|
@@ -7,11 +7,11 @@ module Capybara
|
|
|
7
7
|
module Matchers
|
|
8
8
|
class HaveAncestor < CountableWrappedElementMatcher
|
|
9
9
|
def element_matches?(el)
|
|
10
|
-
el.assert_ancestor(*@args, &@filter_block)
|
|
10
|
+
el.assert_ancestor(*@args, **session_query_options, &@filter_block)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def element_does_not_match?(el)
|
|
14
|
-
el.assert_no_ancestor(*@args, &@filter_block)
|
|
14
|
+
el.assert_no_ancestor(*@args, **session_query_options, &@filter_block)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def description
|
|
@@ -19,7 +19,8 @@ module Capybara
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def query
|
|
22
|
-
@query ||= Capybara::Queries::AncestorQuery.new(*session_query_args, &@filter_block)
|
|
22
|
+
# @query ||= Capybara::Queries::AncestorQuery.new(*session_query_args, &@filter_block)
|
|
23
|
+
@query ||= Capybara::Queries::AncestorQuery.new(*session_query_args, **session_query_options, &@filter_block)
|
|
23
24
|
end
|
|
24
25
|
end
|
|
25
26
|
end
|
|
@@ -7,11 +7,11 @@ module Capybara
|
|
|
7
7
|
module Matchers
|
|
8
8
|
class HaveCurrentPath < WrappedElementMatcher
|
|
9
9
|
def element_matches?(el)
|
|
10
|
-
el.assert_current_path(
|
|
10
|
+
el.assert_current_path(current_path, **@kw_args, &@filter_block)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def element_does_not_match?(el)
|
|
14
|
-
el.assert_no_current_path(
|
|
14
|
+
el.assert_no_current_path(current_path, **@kw_args, &@filter_block)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def description
|
|
@@ -6,12 +6,20 @@ module Capybara
|
|
|
6
6
|
module RSpecMatchers
|
|
7
7
|
module Matchers
|
|
8
8
|
class HaveSelector < CountableWrappedElementMatcher
|
|
9
|
+
def initialize(*args, **kw_args, &filter_block)
|
|
10
|
+
super
|
|
11
|
+
if (RUBY_VERSION >= '2.7') && (@args.size < 2) && @kw_args.keys.any?(String) # rubocop:disable Style/GuardClause
|
|
12
|
+
@args.push(@kw_args)
|
|
13
|
+
@kw_args = {}
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
9
17
|
def element_matches?(el)
|
|
10
|
-
el.assert_selector(*@args, &@filter_block)
|
|
18
|
+
el.assert_selector(*@args, **session_query_options, &@filter_block)
|
|
11
19
|
end
|
|
12
20
|
|
|
13
21
|
def element_does_not_match?(el)
|
|
14
|
-
el.assert_no_selector(*@args, &@filter_block)
|
|
22
|
+
el.assert_no_selector(*@args, **session_query_options, &@filter_block)
|
|
15
23
|
end
|
|
16
24
|
|
|
17
25
|
def description
|
|
@@ -19,13 +27,13 @@ module Capybara
|
|
|
19
27
|
end
|
|
20
28
|
|
|
21
29
|
def query
|
|
22
|
-
@query ||= Capybara::Queries::SelectorQuery.new(*session_query_args, &@filter_block)
|
|
30
|
+
@query ||= Capybara::Queries::SelectorQuery.new(*session_query_args, **session_query_options, &@filter_block)
|
|
23
31
|
end
|
|
24
32
|
end
|
|
25
33
|
|
|
26
34
|
class HaveAllSelectors < WrappedElementMatcher
|
|
27
35
|
def element_matches?(el)
|
|
28
|
-
el.assert_all_of_selectors(*@args, &@filter_block)
|
|
36
|
+
el.assert_all_of_selectors(*@args, **session_query_options, &@filter_block)
|
|
29
37
|
end
|
|
30
38
|
|
|
31
39
|
def does_not_match?(_actual)
|
|
@@ -39,7 +47,7 @@ module Capybara
|
|
|
39
47
|
|
|
40
48
|
class HaveNoSelectors < WrappedElementMatcher
|
|
41
49
|
def element_matches?(el)
|
|
42
|
-
el.assert_none_of_selectors(*@args, &@filter_block)
|
|
50
|
+
el.assert_none_of_selectors(*@args, **session_query_options, &@filter_block)
|
|
43
51
|
end
|
|
44
52
|
|
|
45
53
|
def does_not_match?(_actual)
|
|
@@ -53,11 +61,11 @@ module Capybara
|
|
|
53
61
|
|
|
54
62
|
class HaveAnySelectors < WrappedElementMatcher
|
|
55
63
|
def element_matches?(el)
|
|
56
|
-
el.assert_any_of_selectors(*@args, &@filter_block)
|
|
64
|
+
el.assert_any_of_selectors(*@args, **session_query_options, &@filter_block)
|
|
57
65
|
end
|
|
58
66
|
|
|
59
67
|
def does_not_match?(_actual)
|
|
60
|
-
el.assert_none_of_selectors(*@args, &@filter_block)
|
|
68
|
+
el.assert_none_of_selectors(*@args, **session_query_options, &@filter_block)
|
|
61
69
|
end
|
|
62
70
|
|
|
63
71
|
def description
|
|
@@ -7,11 +7,11 @@ module Capybara
|
|
|
7
7
|
module Matchers
|
|
8
8
|
class HaveSibling < CountableWrappedElementMatcher
|
|
9
9
|
def element_matches?(el)
|
|
10
|
-
el.assert_sibling(*@args, &@filter_block)
|
|
10
|
+
el.assert_sibling(*@args, **session_query_options, &@filter_block)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def element_does_not_match?(el)
|
|
14
|
-
el.assert_no_sibling(*@args, &@filter_block)
|
|
14
|
+
el.assert_no_sibling(*@args, **session_query_options, &@filter_block)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def description
|
|
@@ -19,7 +19,7 @@ module Capybara
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def query
|
|
22
|
-
@query ||= Capybara::Queries::SiblingQuery.new(*session_query_args, &@filter_block)
|
|
22
|
+
@query ||= Capybara::Queries::SiblingQuery.new(*session_query_args, **session_query_options, &@filter_block)
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
end
|
|
@@ -7,15 +7,15 @@ module Capybara
|
|
|
7
7
|
module Matchers
|
|
8
8
|
class HaveText < CountableWrappedElementMatcher
|
|
9
9
|
def element_matches?(el)
|
|
10
|
-
el.assert_text(*@args)
|
|
10
|
+
el.assert_text(*@args, **@kw_args)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def element_does_not_match?(el)
|
|
14
|
-
el.assert_no_text(*@args)
|
|
14
|
+
el.assert_no_text(*@args, **@kw_args)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def description
|
|
18
|
-
"text #{format(text)}"
|
|
18
|
+
"have text #{format(text)}"
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def format(content)
|
|
@@ -7,11 +7,11 @@ module Capybara
|
|
|
7
7
|
module Matchers
|
|
8
8
|
class HaveTitle < WrappedElementMatcher
|
|
9
9
|
def element_matches?(el)
|
|
10
|
-
el.assert_title(*@args)
|
|
10
|
+
el.assert_title(*@args, **@kw_args)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def element_does_not_match?(el)
|
|
14
|
-
el.assert_no_title(*@args)
|
|
14
|
+
el.assert_no_title(*@args, **@kw_args)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def description
|
|
@@ -7,11 +7,11 @@ module Capybara
|
|
|
7
7
|
module Matchers
|
|
8
8
|
class MatchSelector < HaveSelector
|
|
9
9
|
def element_matches?(el)
|
|
10
|
-
el.assert_matches_selector(*@args, &@filter_block)
|
|
10
|
+
el.assert_matches_selector(*@args, **session_query_options, &@filter_block)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def element_does_not_match?(el)
|
|
14
|
-
el.assert_not_matches_selector(*@args, &@filter_block)
|
|
14
|
+
el.assert_not_matches_selector(*@args, **session_query_options, &@filter_block)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def description
|
|
@@ -19,7 +19,7 @@ module Capybara
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def query
|
|
22
|
-
@query ||= Capybara::Queries::MatchQuery.new(*session_query_args, &@filter_block)
|
|
22
|
+
@query ||= Capybara::Queries::MatchQuery.new(*session_query_args, **session_query_options, &@filter_block)
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
end
|
|
@@ -6,8 +6,13 @@ module Capybara
|
|
|
6
6
|
module RSpecMatchers
|
|
7
7
|
module Matchers
|
|
8
8
|
class MatchStyle < WrappedElementMatcher
|
|
9
|
+
def initialize(styles = nil, **kw_args, &filter_block)
|
|
10
|
+
styles, kw_args = kw_args, {} if styles.nil?
|
|
11
|
+
super(styles, **kw_args, &filter_block)
|
|
12
|
+
end
|
|
13
|
+
|
|
9
14
|
def element_matches?(el)
|
|
10
|
-
el.assert_matches_style(*@args)
|
|
15
|
+
el.assert_matches_style(*@args, **@kw_args)
|
|
11
16
|
end
|
|
12
17
|
|
|
13
18
|
def does_not_match?(_actual)
|
|
@@ -28,7 +33,7 @@ module Capybara
|
|
|
28
33
|
##
|
|
29
34
|
# @deprecated
|
|
30
35
|
class HaveStyle < MatchStyle
|
|
31
|
-
def initialize(*args, &filter_block)
|
|
36
|
+
def initialize(*args, **kw_args, &filter_block)
|
|
32
37
|
warn 'HaveStyle matcher is deprecated, please use the MatchStyle matcher instead'
|
|
33
38
|
super
|
|
34
39
|
end
|
data/lib/capybara/selector.rb
CHANGED
|
@@ -7,7 +7,7 @@ require 'capybara/selector/definition'
|
|
|
7
7
|
#
|
|
8
8
|
# All Selectors below support the listed selector specific filters in addition to the following system-wide filters
|
|
9
9
|
# * :id (String, Regexp, XPath::Expression) - Matches the id attribute
|
|
10
|
-
# * :class (String, Array<String>, Regexp, XPath::Expression) - Matches the class(es) provided
|
|
10
|
+
# * :class (String, Array<String | Regexp>, Regexp, XPath::Expression) - Matches the class(es) provided
|
|
11
11
|
# * :style (String, Regexp, Hash<String, String>) - Match on elements style
|
|
12
12
|
# * :above (Element) - Match elements above the passed element on the page
|
|
13
13
|
# * :below (Element) - Match elements below the passed element on the page
|
|
@@ -40,6 +40,7 @@ require 'capybara/selector/definition'
|
|
|
40
40
|
# * :disabled (Boolean, :all) - Match disabled field? (Default: false)
|
|
41
41
|
# * :multiple (Boolean) - Match fields that accept multiple values
|
|
42
42
|
# * :valid (Boolean) - Match fields that are valid/invalid according to HTML5 form validation
|
|
43
|
+
# * :validation_message (String, Regexp) - Matches the elements current validationMessage
|
|
43
44
|
#
|
|
44
45
|
# * **:fieldset** - Select fieldset elements
|
|
45
46
|
# * Locator: Matches id, {Capybara.configure test_id}, or contents of wrapped legend
|
|
@@ -79,6 +80,7 @@ require 'capybara/selector/definition'
|
|
|
79
80
|
# * :disabled (Boolean, :all) - Match disabled field? (Default: false)
|
|
80
81
|
# * :multiple (Boolean) - Match fields that accept multiple values
|
|
81
82
|
# * :valid (Boolean) - Match fields that are valid/invalid according to HTML5 form validation
|
|
83
|
+
# * :validation_message (String, Regexp) - Matches the elements current validationMessage
|
|
82
84
|
#
|
|
83
85
|
# * **:radio_button** - Find radio buttons
|
|
84
86
|
# * Locator: Match id, {Capybara.configure test_id} attribute, name, or associated label text
|
|
@@ -108,6 +110,8 @@ require 'capybara/selector/definition'
|
|
|
108
110
|
# * :disabled (Boolean, :all) - Match disabled field? (Default: false)
|
|
109
111
|
# * :multiple (Boolean) - Match fields that accept multiple values
|
|
110
112
|
# * :options (Array<String>) - Exact match options
|
|
113
|
+
# * :enabled_options (Array<String>) - Exact match enabled options
|
|
114
|
+
# * :disabled_options (Array<String>) - Exact match disabled options
|
|
111
115
|
# * :with_options (Array<String>) - Partial match options
|
|
112
116
|
# * :selected (String, Array<String>) - Match the selection(s)
|
|
113
117
|
# * :with_selected (String, Array<String>) - Partial match the selection(s)
|
|
@@ -167,7 +171,7 @@ require 'capybara/selector/definition'
|
|
|
167
171
|
# * Filters:
|
|
168
172
|
# * :\<any> (String, Regexp) - Match on any specified element attribute
|
|
169
173
|
#
|
|
170
|
-
class Capybara::Selector; end
|
|
174
|
+
class Capybara::Selector; end # rubocop:disable Lint/EmptyClass
|
|
171
175
|
|
|
172
176
|
Capybara::Selector::FilterSet.add(:_field) do
|
|
173
177
|
node_filter(:checked, :boolean) { |node, value| !(value ^ node.checked?) }
|
|
@@ -176,6 +180,12 @@ Capybara::Selector::FilterSet.add(:_field) do
|
|
|
176
180
|
node_filter(:valid, :boolean) { |node, value| node.evaluate_script('this.validity.valid') == value }
|
|
177
181
|
node_filter(:name) { |node, value| !value.is_a?(Regexp) || value.match?(node[:name]) }
|
|
178
182
|
node_filter(:placeholder) { |node, value| !value.is_a?(Regexp) || value.match?(node[:placeholder]) }
|
|
183
|
+
node_filter(:validation_message) do |node, msg|
|
|
184
|
+
vm = node[:validationMessage]
|
|
185
|
+
(msg.is_a?(Regexp) ? msg.match?(vm) : vm == msg.to_s).tap do |res|
|
|
186
|
+
add_error("Expected validation message to be #{msg.inspect} but was #{vm}") unless res
|
|
187
|
+
end
|
|
188
|
+
end
|
|
179
189
|
|
|
180
190
|
expression_filter(:name) do |xpath, val|
|
|
181
191
|
builder(xpath).add_attribute_conditions(name: val)
|
|
@@ -196,7 +206,7 @@ Capybara::Selector::FilterSet.add(:_field) do
|
|
|
196
206
|
desc
|
|
197
207
|
end
|
|
198
208
|
|
|
199
|
-
describe(:node_filters) do |checked: nil, unchecked: nil, disabled: nil, valid: nil, **|
|
|
209
|
+
describe(:node_filters) do |checked: nil, unchecked: nil, disabled: nil, valid: nil, validation_message: nil, **|
|
|
200
210
|
desc, states = +'', []
|
|
201
211
|
states << 'checked' if checked || (unchecked == false)
|
|
202
212
|
states << 'not checked' if unchecked || (checked == false)
|
|
@@ -204,6 +214,7 @@ Capybara::Selector::FilterSet.add(:_field) do
|
|
|
204
214
|
desc << " that is #{states.join(' and ')}" unless states.empty?
|
|
205
215
|
desc << ' that is valid' if valid == true
|
|
206
216
|
desc << ' that is invalid' if valid == false
|
|
217
|
+
desc << " with validation message #{validation_message.to_s.inspect}" if validation_message
|
|
207
218
|
desc
|
|
208
219
|
end
|
|
209
220
|
end
|
|
@@ -74,7 +74,7 @@ module Capybara
|
|
|
74
74
|
end.join
|
|
75
75
|
end
|
|
76
76
|
else
|
|
77
|
-
cls = Array(classes).group_by { |cl| cl.match?(/^!(?!!!)/) }
|
|
77
|
+
cls = Array(classes).reject { |c| c.is_a? Regexp }.group_by { |cl| cl.match?(/^!(?!!!)/) }
|
|
78
78
|
[(cls[false].to_a.map { |cl| ".#{Capybara::Selector::CSS.escape(cl.sub(/^!!/, ''))}" } +
|
|
79
79
|
cls[true].to_a.map { |cl| ":not(.#{Capybara::Selector::CSS.escape(cl.slice(1..-1))})" }).join]
|
|
80
80
|
end
|
|
@@ -15,6 +15,8 @@ module Capybara
|
|
|
15
15
|
def add_attribute_conditions(**conditions)
|
|
16
16
|
@expression = conditions.inject(expression) do |xp, (name, value)|
|
|
17
17
|
conditions = name == :class ? class_conditions(value) : attribute_conditions(name => value)
|
|
18
|
+
return xp if conditions.nil?
|
|
19
|
+
|
|
18
20
|
if xp.is_a? XPath::Expression
|
|
19
21
|
xp[conditions]
|
|
20
22
|
else
|
|
@@ -47,7 +49,7 @@ module Capybara
|
|
|
47
49
|
when XPath::Expression, Regexp
|
|
48
50
|
attribute_conditions(class: classes)
|
|
49
51
|
else
|
|
50
|
-
Array(classes).map do |klass|
|
|
52
|
+
Array(classes).reject { |c| c.is_a? Regexp }.map do |klass|
|
|
51
53
|
if klass.match?(/^!(?!!!)/)
|
|
52
54
|
!XPath.attr(:class).contains_word(klass.slice(1..-1))
|
|
53
55
|
else
|
|
@@ -10,11 +10,12 @@ module Capybara
|
|
|
10
10
|
class Selector
|
|
11
11
|
class Definition
|
|
12
12
|
attr_reader :name, :expressions
|
|
13
|
+
|
|
13
14
|
extend Forwardable
|
|
14
15
|
|
|
15
16
|
def initialize(name, locator_type: nil, raw_locator: false, supports_exact: nil, &block)
|
|
16
17
|
@name = name
|
|
17
|
-
@filter_set = Capybara::Selector::FilterSet.add(name)
|
|
18
|
+
@filter_set = Capybara::Selector::FilterSet.add(name)
|
|
18
19
|
@match = nil
|
|
19
20
|
@label = nil
|
|
20
21
|
@failure_message = nil
|
|
@@ -82,7 +83,7 @@ module Capybara
|
|
|
82
83
|
# Automatic selector detection
|
|
83
84
|
#
|
|
84
85
|
# @yield [locator] This block takes the passed in locator string and returns whether or not it matches the selector
|
|
85
|
-
# @yieldparam [String], locator The locator string used to
|
|
86
|
+
# @yieldparam [String], locator The locator string used to determine if it matches the selector
|
|
86
87
|
# @yieldreturn [Boolean] Whether this selector matches the locator string
|
|
87
88
|
# @return [#call] The block that will be used to detect selector match
|
|
88
89
|
#
|
|
@@ -177,7 +178,7 @@ module Capybara
|
|
|
177
178
|
def_delegator :@filter_set, :describe
|
|
178
179
|
|
|
179
180
|
def describe_expression_filters(&block)
|
|
180
|
-
if
|
|
181
|
+
if block
|
|
181
182
|
describe(:expression_filters, &block)
|
|
182
183
|
else
|
|
183
184
|
describe(:expression_filters) do |**options|
|
|
@@ -189,7 +190,7 @@ module Capybara
|
|
|
189
190
|
def describe_all_expression_filters(**opts)
|
|
190
191
|
expression_filters.map do |ef_name, ef|
|
|
191
192
|
if ef.matcher?
|
|
192
|
-
|
|
193
|
+
handled_custom_options(ef, opts).map { |option, value| " with #{ef_name}[#{option} => #{value}]" }.join
|
|
193
194
|
elsif opts.key?(ef_name)
|
|
194
195
|
" with #{ef_name} #{opts[ef_name]}"
|
|
195
196
|
end
|
|
@@ -214,7 +215,7 @@ module Capybara
|
|
|
214
215
|
end
|
|
215
216
|
|
|
216
217
|
def default_visibility(fallback = Capybara.ignore_hidden_elements, options = {})
|
|
217
|
-
vis = if @default_visibility
|
|
218
|
+
vis = if @default_visibility.respond_to?(:call)
|
|
218
219
|
@default_visibility.call(options)
|
|
219
220
|
else
|
|
220
221
|
@default_visibility
|
|
@@ -251,14 +252,15 @@ module Capybara
|
|
|
251
252
|
|
|
252
253
|
private
|
|
253
254
|
|
|
254
|
-
def
|
|
255
|
-
|
|
256
|
-
filter.handles_option?(
|
|
255
|
+
def handled_custom_options(filter, options)
|
|
256
|
+
options.select do |option, _|
|
|
257
|
+
filter.handles_option?(option) && !::Capybara::Queries::SelectorQuery::VALID_KEYS.include?(option)
|
|
257
258
|
end
|
|
258
259
|
end
|
|
259
260
|
|
|
260
261
|
def parameter_names(block)
|
|
261
|
-
|
|
262
|
+
key_types = %i[key keyreq]
|
|
263
|
+
block.parameters.select { |(type, _name)| key_types.include? type }.map { |(_type, name)| name }
|
|
262
264
|
end
|
|
263
265
|
|
|
264
266
|
def expression(type, allowed_filters, &block)
|
|
@@ -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
|