capybara 3.30.0 → 3.33.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +72 -0
  3. data/README.md +10 -3
  4. data/lib/capybara.rb +17 -7
  5. data/lib/capybara/cucumber.rb +1 -1
  6. data/lib/capybara/dsl.rb +10 -2
  7. data/lib/capybara/minitest.rb +232 -144
  8. data/lib/capybara/minitest/spec.rb +153 -97
  9. data/lib/capybara/node/actions.rb +16 -20
  10. data/lib/capybara/node/element.rb +13 -8
  11. data/lib/capybara/node/finders.rb +5 -1
  12. data/lib/capybara/node/matchers.rb +28 -21
  13. data/lib/capybara/node/simple.rb +1 -1
  14. data/lib/capybara/queries/base_query.rb +2 -1
  15. data/lib/capybara/queries/selector_query.rb +8 -1
  16. data/lib/capybara/queries/style_query.rb +1 -1
  17. data/lib/capybara/queries/text_query.rb +6 -0
  18. data/lib/capybara/rack_test/browser.rb +3 -1
  19. data/lib/capybara/rack_test/node.rb +34 -9
  20. data/lib/capybara/registration_container.rb +44 -0
  21. data/lib/capybara/registrations/servers.rb +1 -1
  22. data/lib/capybara/result.rb +29 -5
  23. data/lib/capybara/rspec/matcher_proxies.rb +4 -4
  24. data/lib/capybara/rspec/matchers.rb +27 -27
  25. data/lib/capybara/rspec/matchers/base.rb +12 -6
  26. data/lib/capybara/rspec/matchers/count_sugar.rb +2 -1
  27. data/lib/capybara/rspec/matchers/have_ancestor.rb +4 -3
  28. data/lib/capybara/rspec/matchers/have_current_path.rb +2 -2
  29. data/lib/capybara/rspec/matchers/have_selector.rb +15 -7
  30. data/lib/capybara/rspec/matchers/have_sibling.rb +3 -3
  31. data/lib/capybara/rspec/matchers/have_text.rb +3 -3
  32. data/lib/capybara/rspec/matchers/have_title.rb +2 -2
  33. data/lib/capybara/rspec/matchers/match_selector.rb +3 -3
  34. data/lib/capybara/rspec/matchers/match_style.rb +2 -2
  35. data/lib/capybara/rspec/matchers/spatial_sugar.rb +2 -1
  36. data/lib/capybara/selector.rb +12 -1
  37. data/lib/capybara/selector/definition.rb +5 -4
  38. data/lib/capybara/selector/definition/button.rb +1 -0
  39. data/lib/capybara/selector/definition/fillable_field.rb +1 -1
  40. data/lib/capybara/selector/definition/label.rb +1 -1
  41. data/lib/capybara/selector/definition/link.rb +8 -0
  42. data/lib/capybara/selector/definition/select.rb +31 -12
  43. data/lib/capybara/selector/definition/table.rb +1 -1
  44. data/lib/capybara/selector/selector.rb +4 -0
  45. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -1
  46. data/lib/capybara/selenium/atoms/src/getAttribute.js +1 -1
  47. data/lib/capybara/selenium/driver.rb +7 -4
  48. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +8 -10
  49. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +7 -9
  50. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +2 -2
  51. data/lib/capybara/selenium/extensions/html5_drag.rb +24 -8
  52. data/lib/capybara/selenium/node.rb +92 -15
  53. data/lib/capybara/selenium/nodes/chrome_node.rb +4 -11
  54. data/lib/capybara/selenium/nodes/edge_node.rb +1 -1
  55. data/lib/capybara/selenium/nodes/firefox_node.rb +3 -3
  56. data/lib/capybara/selenium/patches/action_pauser.rb +26 -0
  57. data/lib/capybara/selenium/patches/logs.rb +3 -5
  58. data/lib/capybara/session.rb +33 -18
  59. data/lib/capybara/session/config.rb +3 -1
  60. data/lib/capybara/spec/public/test.js +58 -6
  61. data/lib/capybara/spec/session/all_spec.rb +45 -5
  62. data/lib/capybara/spec/session/assert_text_spec.rb +5 -5
  63. data/lib/capybara/spec/session/click_button_spec.rb +11 -0
  64. data/lib/capybara/spec/session/fill_in_spec.rb +29 -0
  65. data/lib/capybara/spec/session/find_spec.rb +11 -8
  66. data/lib/capybara/spec/session/has_button_spec.rb +16 -0
  67. data/lib/capybara/spec/session/has_css_spec.rb +12 -9
  68. data/lib/capybara/spec/session/has_current_path_spec.rb +2 -2
  69. data/lib/capybara/spec/session/has_field_spec.rb +16 -0
  70. data/lib/capybara/spec/session/has_select_spec.rb +32 -4
  71. data/lib/capybara/spec/session/has_selector_spec.rb +4 -4
  72. data/lib/capybara/spec/session/has_text_spec.rb +5 -1
  73. data/lib/capybara/spec/session/node_spec.rb +146 -30
  74. data/lib/capybara/spec/session/window/window_spec.rb +7 -7
  75. data/lib/capybara/spec/spec_helper.rb +2 -2
  76. data/lib/capybara/spec/test_app.rb +14 -18
  77. data/lib/capybara/spec/views/form.erb +13 -2
  78. data/lib/capybara/spec/views/with_dragula.erb +3 -1
  79. data/lib/capybara/spec/views/with_html.erb +2 -2
  80. data/lib/capybara/spec/views/with_js.erb +1 -0
  81. data/lib/capybara/version.rb +1 -1
  82. data/spec/capybara_spec.rb +1 -1
  83. data/spec/dsl_spec.rb +14 -1
  84. data/spec/minitest_spec.rb +1 -1
  85. data/spec/rack_test_spec.rb +13 -1
  86. data/spec/regexp_dissassembler_spec.rb +0 -4
  87. data/spec/result_spec.rb +40 -29
  88. data/spec/rspec/shared_spec_matchers.rb +65 -53
  89. data/spec/selector_spec.rb +1 -1
  90. data/spec/selenium_spec_chrome.rb +6 -3
  91. data/spec/selenium_spec_chrome_remote.rb +2 -0
  92. data/spec/server_spec.rb +41 -49
  93. data/spec/shared_selenium_node.rb +18 -0
  94. data/spec/shared_selenium_session.rb +25 -7
  95. data/spec/spec_helper.rb +1 -1
  96. metadata +5 -3
@@ -2,17 +2,17 @@
2
2
 
3
3
  module Capybara
4
4
  module RSpecMatcherProxies
5
- def all(*args, &block)
5
+ def all(*args, **kwargs, &block)
6
6
  if defined?(::RSpec::Matchers::BuiltIn::All) && args.first.respond_to?(:matches?)
7
7
  ::RSpec::Matchers::BuiltIn::All.new(*args)
8
8
  else
9
- find_all(*args, &block)
9
+ find_all(*args, **kwargs, &block)
10
10
  end
11
11
  end
12
12
 
13
- def within(*args, &block)
13
+ def within(*args, **kwargs, &block)
14
14
  if block_given?
15
- within_element(*args, &block)
15
+ within_element(*args, **kwargs, &block)
16
16
  else
17
17
  be_within(*args)
18
18
  end
@@ -15,45 +15,45 @@ module Capybara
15
15
  # RSpec matcher for whether the element(s) matching a given selector exist.
16
16
  #
17
17
  # @see Capybara::Node::Matchers#assert_selector
18
- def have_selector(*args, &optional_filter_block)
19
- Matchers::HaveSelector.new(*args, &optional_filter_block)
18
+ def have_selector(*args, **kw_args, &optional_filter_block)
19
+ Matchers::HaveSelector.new(*args, **kw_args, &optional_filter_block)
20
20
  end
21
21
 
22
22
  # RSpec matcher for whether the element(s) matching a group of selectors exist.
23
23
  #
24
24
  # @see Capybara::Node::Matchers#assert_all_of_selectors
25
- def have_all_of_selectors(*args, &optional_filter_block)
26
- Matchers::HaveAllSelectors.new(*args, &optional_filter_block)
25
+ def have_all_of_selectors(*args, **kw_args, &optional_filter_block)
26
+ Matchers::HaveAllSelectors.new(*args, **kw_args, &optional_filter_block)
27
27
  end
28
28
 
29
29
  # RSpec matcher for whether no element(s) matching a group of selectors exist.
30
30
  #
31
31
  # @see Capybara::Node::Matchers#assert_none_of_selectors
32
- def have_none_of_selectors(*args, &optional_filter_block)
33
- Matchers::HaveNoSelectors.new(*args, &optional_filter_block)
32
+ def have_none_of_selectors(*args, **kw_args, &optional_filter_block)
33
+ Matchers::HaveNoSelectors.new(*args, **kw_args, &optional_filter_block)
34
34
  end
35
35
 
36
36
  # RSpec matcher for whether the element(s) matching any of a group of selectors exist.
37
37
  #
38
38
  # @see Capybara::Node::Matchers#assert_any_of_selectors
39
- def have_any_of_selectors(*args, &optional_filter_block)
40
- Matchers::HaveAnySelectors.new(*args, &optional_filter_block)
39
+ def have_any_of_selectors(*args, **kw_args, &optional_filter_block)
40
+ Matchers::HaveAnySelectors.new(*args, **kw_args, &optional_filter_block)
41
41
  end
42
42
 
43
43
  # RSpec matcher for whether the current element matches a given selector.
44
44
  #
45
45
  # @see Capybara::Node::Matchers#assert_matches_selector
46
- def match_selector(*args, &optional_filter_block)
47
- Matchers::MatchSelector.new(*args, &optional_filter_block)
46
+ def match_selector(*args, **kw_args, &optional_filter_block)
47
+ Matchers::MatchSelector.new(*args, **kw_args, &optional_filter_block)
48
48
  end
49
49
 
50
50
  %i[css xpath].each do |selector|
51
51
  define_method "have_#{selector}" do |expr, **options, &optional_filter_block|
52
- Matchers::HaveSelector.new(selector, expr, options, &optional_filter_block)
52
+ Matchers::HaveSelector.new(selector, expr, **options, &optional_filter_block)
53
53
  end
54
54
 
55
55
  define_method "match_#{selector}" do |expr, **options, &optional_filter_block|
56
- Matchers::MatchSelector.new(selector, expr, options, &optional_filter_block)
56
+ Matchers::MatchSelector.new(selector, expr, **options, &optional_filter_block)
57
57
  end
58
58
  end
59
59
 
@@ -79,7 +79,7 @@ module Capybara
79
79
 
80
80
  %i[link button field select table].each do |selector|
81
81
  define_method "have_#{selector}" do |locator = nil, **options, &optional_filter_block|
82
- Matchers::HaveSelector.new(selector, locator, options, &optional_filter_block)
82
+ Matchers::HaveSelector.new(selector, locator, **options, &optional_filter_block)
83
83
  end
84
84
  end
85
85
 
@@ -110,7 +110,7 @@ module Capybara
110
110
 
111
111
  %i[checked unchecked].each do |state|
112
112
  define_method "have_#{state}_field" do |locator = nil, **options, &optional_filter_block|
113
- Matchers::HaveSelector.new(:field, locator, options.merge(state => true), &optional_filter_block)
113
+ Matchers::HaveSelector.new(:field, locator, **options.merge(state => true), &optional_filter_block)
114
114
  end
115
115
  end
116
116
 
@@ -127,27 +127,27 @@ module Capybara
127
127
  # RSpec matcher for text content.
128
128
  #
129
129
  # @see Capybara::Node::Matchers#assert_text
130
- def have_text(*args)
131
- Matchers::HaveText.new(*args)
130
+ def have_text(text_or_type, *args, **options)
131
+ Matchers::HaveText.new(text_or_type, *args, **options)
132
132
  end
133
133
  alias_method :have_content, :have_text
134
134
 
135
135
  def have_title(title, **options)
136
- Matchers::HaveTitle.new(title, options)
136
+ Matchers::HaveTitle.new(title, **options)
137
137
  end
138
138
 
139
139
  # RSpec matcher for the current path.
140
140
  #
141
141
  # @see Capybara::SessionMatchers#assert_current_path
142
142
  def have_current_path(path, **options)
143
- Matchers::HaveCurrentPath.new(path, options)
143
+ Matchers::HaveCurrentPath.new(path, **options)
144
144
  end
145
145
 
146
146
  # RSpec matcher for element style.
147
147
  #
148
148
  # @see Capybara::Node::Matchers#matches_style?
149
149
  def match_style(styles, **options)
150
- Matchers::MatchStyle.new(styles, options)
150
+ Matchers::MatchStyle.new(styles, **options)
151
151
  end
152
152
 
153
153
  ##
@@ -161,30 +161,30 @@ module Capybara
161
161
  %w[selector css xpath text title current_path link button
162
162
  field checked_field unchecked_field select table
163
163
  sibling ancestor].each do |matcher_type|
164
- define_method "have_no_#{matcher_type}" do |*args, &optional_filter_block|
165
- Matchers::NegatedMatcher.new(send("have_#{matcher_type}", *args, &optional_filter_block))
164
+ define_method "have_no_#{matcher_type}" do |*args, **kw_args, &optional_filter_block|
165
+ Matchers::NegatedMatcher.new(send("have_#{matcher_type}", *args, **kw_args, &optional_filter_block))
166
166
  end
167
167
  end
168
168
  alias_method :have_no_content, :have_no_text
169
169
 
170
170
  %w[selector css xpath].each do |matcher_type|
171
- define_method "not_match_#{matcher_type}" do |*args, &optional_filter_block|
172
- Matchers::NegatedMatcher.new(send("match_#{matcher_type}", *args, &optional_filter_block))
171
+ define_method "not_match_#{matcher_type}" do |*args, **kw_args, &optional_filter_block|
172
+ Matchers::NegatedMatcher.new(send("match_#{matcher_type}", *args, **kw_args, &optional_filter_block))
173
173
  end
174
174
  end
175
175
 
176
176
  # RSpec matcher for whether sibling element(s) matching a given selector exist.
177
177
  #
178
178
  # @see Capybara::Node::Matchers#assert_sibling
179
- def have_sibling(*args, &optional_filter_block)
180
- Matchers::HaveSibling.new(*args, &optional_filter_block)
179
+ def have_sibling(*args, **kw_args, &optional_filter_block)
180
+ Matchers::HaveSibling.new(*args, **kw_args, &optional_filter_block)
181
181
  end
182
182
 
183
183
  # RSpec matcher for whether ancestor element(s) matching a given selector exist.
184
184
  #
185
185
  # @see Capybara::Node::Matchers#assert_ancestor
186
- def have_ancestor(*args, &optional_filter_block)
187
- Matchers::HaveAncestor.new(*args, &optional_filter_block)
186
+ def have_ancestor(*args, **kw_args, &optional_filter_block)
187
+ Matchers::HaveAncestor.new(*args, **kw_args, &optional_filter_block)
188
188
  end
189
189
 
190
190
  ##
@@ -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
- @args.last[:session_options] = session_options
25
- else
26
- @args.push(session_options: session_options)
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
@@ -29,7 +29,8 @@ module Capybara
29
29
  private
30
30
 
31
31
  def options
32
- (@args.last.is_a?(Hash) ? @args : @args.push({})).last
32
+ # (@args.last.is_a?(Hash) ? @args : @args.push({})).last
33
+ @kw_args
33
34
  end
34
35
  end
35
36
  end
@@ -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(*@args)
10
+ el.assert_current_path(current_path, **@kw_args)
11
11
  end
12
12
 
13
13
  def element_does_not_match?(el)
14
- el.assert_no_current_path(*@args)
14
+ el.assert_no_current_path(current_path, **@kw_args)
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
@@ -7,7 +7,7 @@ module Capybara
7
7
  module Matchers
8
8
  class MatchStyle < WrappedElementMatcher
9
9
  def element_matches?(el)
10
- el.assert_matches_style(*@args)
10
+ el.assert_matches_style(*@args, **@kw_args)
11
11
  end
12
12
 
13
13
  def does_not_match?(_actual)
@@ -28,7 +28,7 @@ module Capybara
28
28
  ##
29
29
  # @deprecated
30
30
  class HaveStyle < MatchStyle
31
- def initialize(*args, &filter_block)
31
+ def initialize(*args, **kw_args, &filter_block)
32
32
  warn 'HaveStyle matcher is deprecated, please use the MatchStyle matcher instead'
33
33
  super
34
34
  end
@@ -31,7 +31,8 @@ module Capybara
31
31
  private
32
32
 
33
33
  def options
34
- (@args.last.is_a?(Hash) ? @args : @args.push({})).last
34
+ # (@args.last.is_a?(Hash) ? @args : @args.push({})).last
35
+ @kw_args
35
36
  end
36
37
  end
37
38
  end
@@ -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)
@@ -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