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