capybara 3.30.0 → 3.35.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|