capybara 3.10.1 → 3.11.0
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 +13 -0
- data/README.md +2 -3
- data/lib/capybara.rb +16 -6
- data/lib/capybara/minitest.rb +8 -9
- data/lib/capybara/node/actions.rb +31 -28
- data/lib/capybara/node/base.rb +2 -1
- data/lib/capybara/node/document_matchers.rb +6 -2
- data/lib/capybara/node/element.rb +10 -10
- data/lib/capybara/node/finders.rb +13 -14
- data/lib/capybara/node/matchers.rb +1 -3
- data/lib/capybara/node/simple.rb +10 -2
- data/lib/capybara/queries/base_query.rb +7 -3
- data/lib/capybara/queries/selector_query.rb +60 -34
- data/lib/capybara/queries/style_query.rb +5 -1
- data/lib/capybara/queries/text_query.rb +2 -2
- data/lib/capybara/queries/title_query.rb +1 -1
- data/lib/capybara/rack_test/node.rb +16 -2
- data/lib/capybara/result.rb +9 -4
- data/lib/capybara/rspec/features.rb +4 -4
- data/lib/capybara/rspec/matcher_proxies.rb +3 -1
- data/lib/capybara/rspec/matchers.rb +25 -287
- data/lib/capybara/rspec/matchers/base.rb +98 -0
- data/lib/capybara/rspec/matchers/become_closed.rb +33 -0
- data/lib/capybara/rspec/matchers/compound.rb +88 -0
- data/lib/capybara/rspec/matchers/have_current_path.rb +29 -0
- data/lib/capybara/rspec/matchers/have_selector.rb +69 -0
- data/lib/capybara/rspec/matchers/have_style.rb +23 -0
- data/lib/capybara/rspec/matchers/have_text.rb +33 -0
- data/lib/capybara/rspec/matchers/have_title.rb +29 -0
- data/lib/capybara/rspec/matchers/match_selector.rb +27 -0
- data/lib/capybara/selector.rb +48 -20
- data/lib/capybara/selector/builders/xpath_builder.rb +3 -3
- data/lib/capybara/selector/css.rb +5 -5
- data/lib/capybara/selector/filters/base.rb +11 -3
- data/lib/capybara/selector/filters/expression_filter.rb +3 -3
- data/lib/capybara/selector/filters/node_filter.rb +16 -2
- data/lib/capybara/selector/regexp_disassembler.rb +116 -17
- data/lib/capybara/selector/selector.rb +52 -26
- data/lib/capybara/selenium/driver.rb +6 -2
- data/lib/capybara/selenium/node.rb +15 -14
- data/lib/capybara/selenium/nodes/marionette_node.rb +19 -5
- data/lib/capybara/selenium/patches/pause_duration_fix.rb +1 -3
- data/lib/capybara/server.rb +6 -1
- data/lib/capybara/server/animation_disabler.rb +1 -1
- data/lib/capybara/session.rb +4 -2
- data/lib/capybara/session/matchers.rb +7 -3
- data/lib/capybara/spec/public/test.js +5 -5
- data/lib/capybara/spec/session/all_spec.rb +5 -0
- data/lib/capybara/spec/session/has_css_spec.rb +4 -4
- data/lib/capybara/spec/session/has_field_spec.rb +17 -0
- data/lib/capybara/spec/session/node_spec.rb +45 -4
- data/lib/capybara/spec/spec_helper.rb +6 -1
- data/lib/capybara/spec/views/frame_child.erb +1 -1
- data/lib/capybara/spec/views/obscured.erb +44 -0
- data/lib/capybara/spec/views/with_html.erb +1 -1
- data/lib/capybara/version.rb +1 -1
- data/spec/rack_test_spec.rb +15 -0
- data/spec/regexp_dissassembler_spec.rb +88 -8
- data/spec/selector_spec.rb +3 -0
- data/spec/selenium_spec_chrome.rb +9 -15
- data/spec/selenium_spec_chrome_remote.rb +3 -2
- data/spec/selenium_spec_firefox_remote.rb +6 -2
- metadata +54 -3
- data/lib/capybara/rspec/compound.rb +0 -86
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'capybara/rspec/matchers/compound'
|
4
|
+
|
5
|
+
module Capybara
|
6
|
+
module RSpecMatchers
|
7
|
+
module Matchers
|
8
|
+
class Base
|
9
|
+
include ::Capybara::RSpecMatchers::Matchers::Compound if defined?(::Capybara::RSpecMatchers::Matchers::Compound)
|
10
|
+
|
11
|
+
attr_reader :failure_message, :failure_message_when_negated
|
12
|
+
|
13
|
+
def initialize(*args, &filter_block)
|
14
|
+
@args = args.dup
|
15
|
+
@filter_block = filter_block
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def session_query_args
|
21
|
+
if @args.last.is_a? Hash
|
22
|
+
@args.last[:session_options] = session_options
|
23
|
+
else
|
24
|
+
@args.push(session_options: session_options)
|
25
|
+
end
|
26
|
+
@args
|
27
|
+
end
|
28
|
+
|
29
|
+
def session_options
|
30
|
+
@context_el ||= nil
|
31
|
+
if @context_el.respond_to? :session_options
|
32
|
+
@context_el.session_options
|
33
|
+
elsif @context_el.respond_to? :current_scope
|
34
|
+
@context_el.current_scope.session_options
|
35
|
+
else
|
36
|
+
Capybara.session_options
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class WrappedElementMatcher < Base
|
42
|
+
def matches?(actual)
|
43
|
+
element_matches?(wrap(actual))
|
44
|
+
rescue Capybara::ExpectationNotMet => err
|
45
|
+
@failure_message = err.message
|
46
|
+
false
|
47
|
+
end
|
48
|
+
|
49
|
+
def does_not_match?(actual)
|
50
|
+
element_does_not_match?(wrap(actual))
|
51
|
+
rescue Capybara::ExpectationNotMet => err
|
52
|
+
@failure_message_when_negated = err.message
|
53
|
+
false
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def wrap(actual)
|
59
|
+
actual = actual.to_capybara_node if actual.respond_to?(:to_capybara_node)
|
60
|
+
@context_el = if actual.respond_to?(:has_selector?)
|
61
|
+
actual
|
62
|
+
else
|
63
|
+
Capybara.string(actual.to_s)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class NegatedMatcher
|
69
|
+
include ::Capybara::RSpecMatchers::Matchers::Compound if defined?(::Capybara::RSpecMatchers::Matchers::Compound)
|
70
|
+
|
71
|
+
def initialize(matcher)
|
72
|
+
super()
|
73
|
+
@matcher = matcher
|
74
|
+
end
|
75
|
+
|
76
|
+
def matches?(actual)
|
77
|
+
@matcher.does_not_match?(actual)
|
78
|
+
end
|
79
|
+
|
80
|
+
def does_not_match?(actual)
|
81
|
+
@matcher.matches?(actual)
|
82
|
+
end
|
83
|
+
|
84
|
+
def description
|
85
|
+
"not #{@matcher.description}"
|
86
|
+
end
|
87
|
+
|
88
|
+
def failure_message
|
89
|
+
@matcher.failure_message_when_negated
|
90
|
+
end
|
91
|
+
|
92
|
+
def failure_message_when_negated
|
93
|
+
@matcher.failure_message
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Capybara
|
4
|
+
module RSpecMatchers
|
5
|
+
module Matchers
|
6
|
+
class BecomeClosed
|
7
|
+
def initialize(options)
|
8
|
+
@options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
def matches?(window)
|
12
|
+
@window = window
|
13
|
+
@wait_time = Capybara::Queries::BaseQuery.wait(@options, window.session.config.default_max_wait_time)
|
14
|
+
timer = Capybara::Helpers.timer(expire_in: @wait_time)
|
15
|
+
while window.exists?
|
16
|
+
return false if timer.expired?
|
17
|
+
|
18
|
+
sleep 0.05
|
19
|
+
end
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def failure_message
|
24
|
+
"expected #{@window.inspect} to become closed after #{@wait_time} seconds"
|
25
|
+
end
|
26
|
+
|
27
|
+
def failure_message_when_negated
|
28
|
+
"expected #{@window.inspect} not to become closed after #{@wait_time} seconds"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if defined?(::RSpec::Expectations::Version)
|
4
|
+
module Capybara
|
5
|
+
module RSpecMatchers
|
6
|
+
module Matchers
|
7
|
+
module Compound
|
8
|
+
include ::RSpec::Matchers::Composable
|
9
|
+
|
10
|
+
def and(matcher)
|
11
|
+
And.new(self, matcher)
|
12
|
+
end
|
13
|
+
|
14
|
+
def and_then(matcher)
|
15
|
+
::RSpec::Matchers::BuiltIn::Compound::And.new(self, matcher)
|
16
|
+
end
|
17
|
+
|
18
|
+
def or(matcher)
|
19
|
+
Or.new(self, matcher)
|
20
|
+
end
|
21
|
+
|
22
|
+
class CapybaraEvaluator
|
23
|
+
def initialize(actual)
|
24
|
+
@actual = actual
|
25
|
+
@match_results = Hash.new { |hsh, matcher| hsh[matcher] = matcher.matches?(@actual) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def matcher_matches?(matcher)
|
29
|
+
@match_results[matcher]
|
30
|
+
end
|
31
|
+
|
32
|
+
def reset
|
33
|
+
@match_results.clear
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# @api private
|
38
|
+
module Synchronizer
|
39
|
+
def match(_expected, actual)
|
40
|
+
@evaluator = CapybaraEvaluator.new(actual)
|
41
|
+
syncer = sync_element(actual)
|
42
|
+
begin
|
43
|
+
syncer.synchronize do
|
44
|
+
@evaluator.reset
|
45
|
+
raise ::Capybara::ElementNotFound unless synchronized_match?
|
46
|
+
|
47
|
+
true
|
48
|
+
end
|
49
|
+
rescue StandardError
|
50
|
+
false
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def sync_element(el)
|
55
|
+
if el.respond_to? :synchronize
|
56
|
+
el
|
57
|
+
elsif el.respond_to? :current_scope
|
58
|
+
el.current_scope
|
59
|
+
else
|
60
|
+
Capybara.string(el)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class And < ::RSpec::Matchers::BuiltIn::Compound::And
|
66
|
+
include Synchronizer
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def synchronized_match?
|
71
|
+
[matcher_1_matches?, matcher_2_matches?].all?
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Or < ::RSpec::Matchers::BuiltIn::Compound::Or
|
76
|
+
include Synchronizer
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def synchronized_match?
|
81
|
+
[matcher_1_matches?, matcher_2_matches?].any?
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'capybara/rspec/matchers/base'
|
4
|
+
|
5
|
+
module Capybara
|
6
|
+
module RSpecMatchers
|
7
|
+
module Matchers
|
8
|
+
class HaveCurrentPath < WrappedElementMatcher
|
9
|
+
def element_matches?(el)
|
10
|
+
el.assert_current_path(*@args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def element_does_not_match?(el)
|
14
|
+
el.assert_no_current_path(*@args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def description
|
18
|
+
"have current path #{current_path.inspect}"
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def current_path
|
24
|
+
@args.first
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'capybara/rspec/matchers/base'
|
4
|
+
|
5
|
+
module Capybara
|
6
|
+
module RSpecMatchers
|
7
|
+
module Matchers
|
8
|
+
class HaveSelector < WrappedElementMatcher
|
9
|
+
def element_matches?(el)
|
10
|
+
el.assert_selector(*@args, &@filter_block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def element_does_not_match?(el)
|
14
|
+
el.assert_no_selector(*@args, &@filter_block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def description
|
18
|
+
"have #{query.description}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def query
|
22
|
+
@query ||= Capybara::Queries::SelectorQuery.new(*session_query_args, &@filter_block)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class HaveAllSelectors < WrappedElementMatcher
|
27
|
+
def element_matches?(el)
|
28
|
+
el.assert_all_of_selectors(*@args, &@filter_block)
|
29
|
+
end
|
30
|
+
|
31
|
+
def does_not_match?(_actual)
|
32
|
+
raise ArgumentError, 'The have_all_selectors matcher does not support use with not_to/should_not'
|
33
|
+
end
|
34
|
+
|
35
|
+
def description
|
36
|
+
'have all selectors'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class HaveNoSelectors < WrappedElementMatcher
|
41
|
+
def element_matches?(el)
|
42
|
+
el.assert_none_of_selectors(*@args, &@filter_block)
|
43
|
+
end
|
44
|
+
|
45
|
+
def does_not_match?(_actual)
|
46
|
+
raise ArgumentError, 'The have_none_of_selectors matcher does not support use with not_to/should_not'
|
47
|
+
end
|
48
|
+
|
49
|
+
def description
|
50
|
+
'have no selectors'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class HaveAnySelectors < WrappedElementMatcher
|
55
|
+
def element_matches?(el)
|
56
|
+
el.assert_any_of_selectors(*@args, &@filter_block)
|
57
|
+
end
|
58
|
+
|
59
|
+
def does_not_match?(_actual)
|
60
|
+
el.assert_none_of_selectors(*@args, &@filter_block)
|
61
|
+
end
|
62
|
+
|
63
|
+
def description
|
64
|
+
'have any selectors'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'capybara/rspec/matchers/base'
|
4
|
+
|
5
|
+
module Capybara
|
6
|
+
module RSpecMatchers
|
7
|
+
module Matchers
|
8
|
+
class HaveStyle < WrappedElementMatcher
|
9
|
+
def element_matches?(el)
|
10
|
+
el.assert_style(*@args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def does_not_match?(_actual)
|
14
|
+
raise ArgumentError, 'The have_style matcher does not support use with not_to/should_not'
|
15
|
+
end
|
16
|
+
|
17
|
+
def description
|
18
|
+
'have style'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'capybara/rspec/matchers/base'
|
4
|
+
|
5
|
+
module Capybara
|
6
|
+
module RSpecMatchers
|
7
|
+
module Matchers
|
8
|
+
class HaveText < WrappedElementMatcher
|
9
|
+
def element_matches?(el)
|
10
|
+
el.assert_text(*@args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def element_does_not_match?(el)
|
14
|
+
el.assert_no_text(*@args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def description
|
18
|
+
"text #{format(text)}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def format(content)
|
22
|
+
content.inspect
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def text
|
28
|
+
@args[0].is_a?(Symbol) ? @args[1] : @args[0]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'capybara/rspec/matchers/base'
|
4
|
+
|
5
|
+
module Capybara
|
6
|
+
module RSpecMatchers
|
7
|
+
module Matchers
|
8
|
+
class HaveTitle < WrappedElementMatcher
|
9
|
+
def element_matches?(el)
|
10
|
+
el.assert_title(*@args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def element_does_not_match?(el)
|
14
|
+
el.assert_no_title(*@args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def description
|
18
|
+
"have title #{title.inspect}"
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def title
|
24
|
+
@args.first
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'capybara/rspec/matchers/have_selector'
|
4
|
+
|
5
|
+
module Capybara
|
6
|
+
module RSpecMatchers
|
7
|
+
module Matchers
|
8
|
+
class MatchSelector < HaveSelector
|
9
|
+
def element_matches?(el)
|
10
|
+
el.assert_matches_selector(*@args, &@filter_block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def element_does_not_match?(el)
|
14
|
+
el.assert_not_matches_selector(*@args, &@filter_block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def description
|
18
|
+
"match #{query.description}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def query
|
22
|
+
@query ||= Capybara::Queries::MatchQuery.new(*session_query_args, &@filter_block)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/capybara/selector.rb
CHANGED
@@ -34,7 +34,8 @@ Capybara.add_selector(:css) do
|
|
34
34
|
end
|
35
35
|
|
36
36
|
Capybara.add_selector(:id) do
|
37
|
-
xpath { |id| XPath.descendant[
|
37
|
+
xpath { |id| XPath.descendant[builder.attribute_conditions(id: id)] }
|
38
|
+
locator_filter { |node, id| id.is_a?(Regexp) ? node[:id] =~ id : true }
|
38
39
|
end
|
39
40
|
|
40
41
|
Capybara.add_selector(:field) do
|
@@ -59,7 +60,10 @@ Capybara.add_selector(:field) do
|
|
59
60
|
|
60
61
|
node_filter(:readonly, :boolean) { |node, value| !(value ^ node.readonly?) }
|
61
62
|
node_filter(:with) do |node, with|
|
62
|
-
|
63
|
+
val = node.value
|
64
|
+
(with.is_a?(Regexp) ? val =~ with : val == with.to_s).tap do |res|
|
65
|
+
add_error("Expected value to be #{with.inspect} but was #{val.inspect}") unless res
|
66
|
+
end
|
63
67
|
end
|
64
68
|
|
65
69
|
describe_expression_filters do |type: nil, **options|
|
@@ -109,16 +113,13 @@ Capybara.add_selector(:link) do
|
|
109
113
|
|
110
114
|
node_filter(:href) do |node, href|
|
111
115
|
# If not a Regexp it's been handled in the main XPath
|
112
|
-
href.is_a?(Regexp) ? node[:href].match(href) : true
|
116
|
+
(href.is_a?(Regexp) ? node[:href].match(href) : true).tap do |res|
|
117
|
+
add_error "Expected href to match #{href.inspect} but it was #{node[:href].inspect}" unless res
|
118
|
+
end
|
113
119
|
end
|
114
120
|
|
115
121
|
expression_filter(:download, valid_values: [true, false, String]) do |expr, download|
|
116
|
-
|
117
|
-
when true then XPath.attr(:download)
|
118
|
-
when false then !XPath.attr(:download)
|
119
|
-
when String then XPath.attr(:download) == download
|
120
|
-
end
|
121
|
-
expr[mod]
|
122
|
+
expr[builder.attribute_conditions(download: download)]
|
122
123
|
end
|
123
124
|
|
124
125
|
describe_expression_filters do |**options|
|
@@ -178,7 +179,9 @@ end
|
|
178
179
|
Capybara.add_selector(:link_or_button) do
|
179
180
|
label 'link or button'
|
180
181
|
xpath do |locator, **options|
|
181
|
-
self.class.all.values_at(:link, :button).map
|
182
|
+
self.class.all.values_at(:link, :button).map do |selector|
|
183
|
+
instance_exec(locator, options, &selector.xpath)
|
184
|
+
end.reduce(:union)
|
182
185
|
end
|
183
186
|
|
184
187
|
node_filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| node.tag_name == 'a' || !(value ^ node.disabled?) }
|
@@ -210,7 +213,10 @@ Capybara.add_selector(:fillable_field) do
|
|
210
213
|
filter_set(:_field, %i[disabled multiple name placeholder])
|
211
214
|
|
212
215
|
node_filter(:with) do |node, with|
|
213
|
-
|
216
|
+
val = node.value
|
217
|
+
(with.is_a?(Regexp) ? val =~ with : val == with.to_s).tap do |res|
|
218
|
+
add_error("Expected value to be #{with.inspect} but was #{val.inspect}") unless res
|
219
|
+
end
|
214
220
|
end
|
215
221
|
|
216
222
|
describe_expression_filters
|
@@ -231,7 +237,12 @@ Capybara.add_selector(:radio_button) do
|
|
231
237
|
|
232
238
|
filter_set(:_field, %i[checked unchecked disabled name])
|
233
239
|
|
234
|
-
node_filter(:option)
|
240
|
+
node_filter(:option) do |node, value|
|
241
|
+
val = node.value
|
242
|
+
(val == value.to_s).tap do |res|
|
243
|
+
add_error("Expected option value to be #{value.inspect} but it was #{val.inspect}") unless res
|
244
|
+
end
|
245
|
+
end
|
235
246
|
|
236
247
|
describe_expression_filters
|
237
248
|
describe_node_filters do |option: nil, **|
|
@@ -249,7 +260,12 @@ Capybara.add_selector(:checkbox) do
|
|
249
260
|
|
250
261
|
filter_set(:_field, %i[checked unchecked disabled name])
|
251
262
|
|
252
|
-
node_filter(:option)
|
263
|
+
node_filter(:option) do |node, value|
|
264
|
+
val = node.value
|
265
|
+
(val == value.to_s).tap do |res|
|
266
|
+
add_error("Expected option value to be #{value.inspect} but it was #{val.inspect}") unless res
|
267
|
+
end
|
268
|
+
end
|
253
269
|
|
254
270
|
describe_expression_filters
|
255
271
|
describe_node_filters do |option: nil, **|
|
@@ -273,23 +289,29 @@ Capybara.add_selector(:select) do
|
|
273
289
|
else
|
274
290
|
node.all(:xpath, './/option', visible: false, wait: false).map { |option| option.text(:all) }
|
275
291
|
end
|
276
|
-
options.sort == actual.sort
|
292
|
+
(options.sort == actual.sort).tap do |res|
|
293
|
+
add_error("Expected options #{options.inspect} found #{actual.inspect}") unless res
|
294
|
+
end
|
277
295
|
end
|
278
296
|
|
279
297
|
expression_filter(:with_options) do |expr, options|
|
280
298
|
options.inject(expr) do |xpath, option|
|
281
|
-
xpath[
|
299
|
+
xpath[self.class.all[:option].call(option)]
|
282
300
|
end
|
283
301
|
end
|
284
302
|
|
285
303
|
node_filter(:selected) do |node, selected|
|
286
304
|
actual = node.all(:xpath, './/option', visible: false, wait: false).select(&:selected?).map { |option| option.text(:all) }
|
287
|
-
Array(selected).sort == actual.sort
|
305
|
+
(Array(selected).sort == actual.sort).tap do |res|
|
306
|
+
add_error("Expected #{selected.inspect} to be selected found #{actual.inspect}") unless res
|
307
|
+
end
|
288
308
|
end
|
289
309
|
|
290
310
|
node_filter(:with_selected) do |node, selected|
|
291
311
|
actual = node.all(:xpath, './/option', visible: false, wait: false).select(&:selected?).map { |option| option.text(:all) }
|
292
|
-
(Array(selected) - actual).empty
|
312
|
+
(Array(selected) - actual).empty?.tap do |res|
|
313
|
+
add_error("Expected at least #{selected.inspect} to be selected found #{actual.inspect}") unless res
|
314
|
+
end
|
293
315
|
end
|
294
316
|
|
295
317
|
describe_expression_filters do |with_options: nil, **opts|
|
@@ -320,12 +342,14 @@ Capybara.add_selector(:datalist_input) do
|
|
320
342
|
|
321
343
|
node_filter(:options) do |node, options|
|
322
344
|
actual = node.find("//datalist[@id=#{node[:list]}]", visible: :all).all(:datalist_option, wait: false).map(&:value)
|
323
|
-
options.sort == actual.sort
|
345
|
+
(options.sort == actual.sort).tap do |res|
|
346
|
+
add_error("Expected #{options.inspect} options found #{actual.inspect}") unless res
|
347
|
+
end
|
324
348
|
end
|
325
349
|
|
326
350
|
expression_filter(:with_options) do |expr, options|
|
327
351
|
options.inject(expr) do |xpath, option|
|
328
|
-
xpath[XPath.attr(:list) == XPath.anywhere(:datalist)[
|
352
|
+
xpath[XPath.attr(:list) == XPath.anywhere(:datalist)[self.class.all[:datalist_option].call(option)].attr(:id)]
|
329
353
|
end
|
330
354
|
end
|
331
355
|
|
@@ -471,7 +495,11 @@ Capybara.add_selector(:element) do
|
|
471
495
|
end
|
472
496
|
|
473
497
|
node_filter(:attributes, matcher: /.+/) do |node, name, val|
|
474
|
-
val.is_a?(Regexp)
|
498
|
+
next true unless val.is_a?(Regexp)
|
499
|
+
|
500
|
+
(node[name] =~ val).tap do |res|
|
501
|
+
add_error("Expected #{name} to match #{val.inspect} but it was #{node[name]}") unless res
|
502
|
+
end
|
475
503
|
end
|
476
504
|
|
477
505
|
describe_expression_filters do |**options|
|