capybara 3.30.0 → 3.35.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +153 -13
  3. data/README.md +9 -4
  4. data/lib/capybara.rb +18 -8
  5. data/lib/capybara/config.rb +4 -6
  6. data/lib/capybara/cucumber.rb +1 -1
  7. data/lib/capybara/driver/base.rb +4 -0
  8. data/lib/capybara/dsl.rb +10 -2
  9. data/lib/capybara/helpers.rb +25 -1
  10. data/lib/capybara/minitest.rb +232 -144
  11. data/lib/capybara/minitest/spec.rb +156 -97
  12. data/lib/capybara/node/actions.rb +16 -21
  13. data/lib/capybara/node/base.rb +6 -6
  14. data/lib/capybara/node/element.rb +14 -13
  15. data/lib/capybara/node/finders.rb +12 -7
  16. data/lib/capybara/node/matchers.rb +36 -27
  17. data/lib/capybara/node/simple.rb +6 -2
  18. data/lib/capybara/queries/ancestor_query.rb +1 -1
  19. data/lib/capybara/queries/base_query.rb +2 -1
  20. data/lib/capybara/queries/current_path_query.rb +14 -4
  21. data/lib/capybara/queries/selector_query.rb +40 -18
  22. data/lib/capybara/queries/sibling_query.rb +1 -1
  23. data/lib/capybara/queries/style_query.rb +1 -1
  24. data/lib/capybara/queries/text_query.rb +7 -1
  25. data/lib/capybara/rack_test/browser.rb +9 -3
  26. data/lib/capybara/rack_test/driver.rb +1 -0
  27. data/lib/capybara/rack_test/form.rb +1 -1
  28. data/lib/capybara/rack_test/node.rb +35 -10
  29. data/lib/capybara/registration_container.rb +44 -0
  30. data/lib/capybara/registrations/drivers.rb +18 -12
  31. data/lib/capybara/registrations/patches/puma_ssl.rb +3 -1
  32. data/lib/capybara/registrations/servers.rb +3 -2
  33. data/lib/capybara/result.rb +35 -15
  34. data/lib/capybara/rspec.rb +2 -0
  35. data/lib/capybara/rspec/matcher_proxies.rb +5 -5
  36. data/lib/capybara/rspec/matchers.rb +33 -32
  37. data/lib/capybara/rspec/matchers/base.rb +12 -6
  38. data/lib/capybara/rspec/matchers/count_sugar.rb +2 -1
  39. data/lib/capybara/rspec/matchers/have_ancestor.rb +4 -3
  40. data/lib/capybara/rspec/matchers/have_current_path.rb +2 -2
  41. data/lib/capybara/rspec/matchers/have_selector.rb +15 -7
  42. data/lib/capybara/rspec/matchers/have_sibling.rb +3 -3
  43. data/lib/capybara/rspec/matchers/have_text.rb +3 -3
  44. data/lib/capybara/rspec/matchers/have_title.rb +2 -2
  45. data/lib/capybara/rspec/matchers/match_selector.rb +3 -3
  46. data/lib/capybara/rspec/matchers/match_style.rb +7 -2
  47. data/lib/capybara/rspec/matchers/spatial_sugar.rb +2 -1
  48. data/lib/capybara/selector.rb +14 -3
  49. data/lib/capybara/selector/builders/css_builder.rb +1 -1
  50. data/lib/capybara/selector/builders/xpath_builder.rb +3 -1
  51. data/lib/capybara/selector/definition.rb +11 -9
  52. data/lib/capybara/selector/definition/button.rb +26 -14
  53. data/lib/capybara/selector/definition/css.rb +1 -1
  54. data/lib/capybara/selector/definition/datalist_input.rb +1 -1
  55. data/lib/capybara/selector/definition/element.rb +2 -1
  56. data/lib/capybara/selector/definition/fillable_field.rb +1 -1
  57. data/lib/capybara/selector/definition/label.rb +2 -2
  58. data/lib/capybara/selector/definition/link.rb +8 -0
  59. data/lib/capybara/selector/definition/select.rb +32 -13
  60. data/lib/capybara/selector/definition/table.rb +1 -1
  61. data/lib/capybara/selector/definition/table_row.rb +2 -2
  62. data/lib/capybara/selector/filter_set.rb +2 -2
  63. data/lib/capybara/selector/selector.rb +9 -1
  64. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -1
  65. data/lib/capybara/selenium/atoms/src/getAttribute.js +1 -1
  66. data/lib/capybara/selenium/atoms/src/isDisplayed.js +1 -1
  67. data/lib/capybara/selenium/driver.rb +52 -7
  68. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +10 -12
  69. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +9 -11
  70. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +3 -3
  71. data/lib/capybara/selenium/extensions/find.rb +4 -4
  72. data/lib/capybara/selenium/extensions/html5_drag.rb +24 -8
  73. data/lib/capybara/selenium/extensions/scroll.rb +8 -10
  74. data/lib/capybara/selenium/logger_suppressor.rb +8 -2
  75. data/lib/capybara/selenium/node.rb +96 -16
  76. data/lib/capybara/selenium/nodes/chrome_node.rb +27 -16
  77. data/lib/capybara/selenium/nodes/edge_node.rb +1 -1
  78. data/lib/capybara/selenium/nodes/firefox_node.rb +9 -4
  79. data/lib/capybara/selenium/nodes/safari_node.rb +1 -1
  80. data/lib/capybara/selenium/patches/action_pauser.rb +26 -0
  81. data/lib/capybara/selenium/patches/atoms.rb +4 -4
  82. data/lib/capybara/selenium/patches/logs.rb +7 -9
  83. data/lib/capybara/server/animation_disabler.rb +8 -3
  84. data/lib/capybara/server/middleware.rb +4 -2
  85. data/lib/capybara/session.rb +53 -29
  86. data/lib/capybara/session/config.rb +3 -1
  87. data/lib/capybara/session/matchers.rb +11 -11
  88. data/lib/capybara/spec/public/test.js +64 -7
  89. data/lib/capybara/spec/session/accept_alert_spec.rb +1 -1
  90. data/lib/capybara/spec/session/all_spec.rb +45 -5
  91. data/lib/capybara/spec/session/assert_text_spec.rb +5 -5
  92. data/lib/capybara/spec/session/check_spec.rb +6 -0
  93. data/lib/capybara/spec/session/click_button_spec.rb +11 -0
  94. data/lib/capybara/spec/session/click_link_or_button_spec.rb +9 -0
  95. data/lib/capybara/spec/session/current_url_spec.rb +11 -1
  96. data/lib/capybara/spec/session/fill_in_spec.rb +29 -0
  97. data/lib/capybara/spec/session/find_spec.rb +11 -8
  98. data/lib/capybara/spec/session/has_button_spec.rb +51 -0
  99. data/lib/capybara/spec/session/has_css_spec.rb +14 -10
  100. data/lib/capybara/spec/session/has_current_path_spec.rb +15 -2
  101. data/lib/capybara/spec/session/has_field_spec.rb +16 -0
  102. data/lib/capybara/spec/session/has_select_spec.rb +32 -4
  103. data/lib/capybara/spec/session/has_selector_spec.rb +4 -4
  104. data/lib/capybara/spec/session/has_text_spec.rb +5 -12
  105. data/lib/capybara/spec/session/html_spec.rb +1 -1
  106. data/lib/capybara/spec/session/matches_style_spec.rb +2 -2
  107. data/lib/capybara/spec/session/node_spec.rb +169 -33
  108. data/lib/capybara/spec/session/refresh_spec.rb +2 -1
  109. data/lib/capybara/spec/session/save_page_spec.rb +4 -4
  110. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +1 -1
  111. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +1 -1
  112. data/lib/capybara/spec/session/window/window_spec.rb +8 -8
  113. data/lib/capybara/spec/session/window/windows_spec.rb +1 -1
  114. data/lib/capybara/spec/spec_helper.rb +13 -14
  115. data/lib/capybara/spec/test_app.rb +23 -21
  116. data/lib/capybara/spec/views/form.erb +36 -3
  117. data/lib/capybara/spec/views/with_animation.erb +8 -0
  118. data/lib/capybara/spec/views/with_dragula.erb +3 -1
  119. data/lib/capybara/spec/views/with_html.erb +2 -2
  120. data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
  121. data/lib/capybara/spec/views/with_js.erb +3 -0
  122. data/lib/capybara/spec/views/with_sortable_js.erb +1 -1
  123. data/lib/capybara/version.rb +1 -1
  124. data/lib/capybara/window.rb +3 -7
  125. data/spec/basic_node_spec.rb +9 -8
  126. data/spec/capybara_spec.rb +1 -1
  127. data/spec/dsl_spec.rb +14 -1
  128. data/spec/fixtures/selenium_driver_rspec_success.rb +1 -1
  129. data/spec/minitest_spec.rb +3 -2
  130. data/spec/rack_test_spec.rb +28 -6
  131. data/spec/regexp_dissassembler_spec.rb +0 -4
  132. data/spec/result_spec.rb +40 -29
  133. data/spec/rspec/features_spec.rb +3 -1
  134. data/spec/rspec/scenarios_spec.rb +4 -0
  135. data/spec/rspec/shared_spec_matchers.rb +63 -51
  136. data/spec/rspec_spec.rb +4 -0
  137. data/spec/selector_spec.rb +17 -2
  138. data/spec/selenium_spec_chrome.rb +45 -21
  139. data/spec/selenium_spec_chrome_remote.rb +7 -1
  140. data/spec/selenium_spec_firefox.rb +15 -13
  141. data/spec/server_spec.rb +60 -49
  142. data/spec/shared_selenium_node.rb +18 -0
  143. data/spec/shared_selenium_session.rb +98 -7
  144. data/spec/spec_helper.rb +1 -1
  145. metadata +50 -14
  146. data/lib/capybara/spec/session/source_spec.rb +0 -0
@@ -24,12 +24,13 @@ module Capybara
24
24
  # @option options [Boolean] normalize_ws
25
25
  # Whether the `text`/`exact_text` options are compared against elment text with whitespace normalized or as returned by the driver.
26
26
  # Defaults to {Capybara.configure default_normalize_ws}.
27
- # @option options [Boolean, Symbol] visible Only find elements with the specified visibility:
28
- # * true - only finds visible elements.
29
- # * false - finds invisible _and_ visible elements.
30
- # * :all - same as false; finds visible and invisible elements.
31
- # * :hidden - only finds invisible elements.
32
- # * :visible - same as true; only finds visible elements.
27
+ # @option options [Boolean, Symbol] visible
28
+ # Only find elements with the specified visibility. Defaults to behavior indicated by {Capybara.configure ignore_hidden_elements}.
29
+ # * true - only finds visible elements.
30
+ # * false - finds invisible _and_ visible elements.
31
+ # * :all - same as false; finds visible and invisible elements.
32
+ # * :hidden - only finds invisible elements.
33
+ # * :visible - same as true; only finds visible elements.
33
34
  # @option options [Boolean] obscured Only find elements with the specified obscured state:
34
35
  # * true - only find elements whose centerpoint is not in the viewport or is obscured by another non-descendant element.
35
36
  # * false - only find elements whose centerpoint is in the viewport and is not obscured by other non-descendant elements.
@@ -235,13 +236,16 @@ module Capybara
235
236
  # @option options [Integer] maximum Maximum number of matches that are expected to be found
236
237
  # @option options [Integer] minimum Minimum number of matches that are expected to be found
237
238
  # @option options [Range] between Number of matches found must be within the given range
239
+ # @option options [Boolean] allow_reload Beta feature - May be removed in any version.
240
+ # When `true` allows elements to be reloaded if they become stale. This is an advanced behavior and should only be used
241
+ # if you fully understand the potential ramifications. The results can be confusing on dynamic pages. Defaults to `false`
238
242
  # @overload all([kind = Capybara.default_selector], locator = nil, **options)
239
243
  # @overload all([kind = Capybara.default_selector], locator = nil, **options, &filter_block)
240
244
  # @yieldparam element [Capybara::Node::Element] The element being considered for inclusion in the results
241
245
  # @yieldreturn [Boolean] Should the element be considered in the results?
242
246
  # @return [Capybara::Result] A collection of found elements
243
247
  # @raise [Capybara::ExpectationNotMet] The number of elements found doesn't match the specified conditions
244
- def all(*args, **options, &optional_filter_block)
248
+ def all(*args, allow_reload: false, **options, &optional_filter_block)
245
249
  minimum_specified = options_include_minimum?(options)
246
250
  options = { minimum: 1 }.merge(options) unless minimum_specified
247
251
  options[:session_options] = session_options
@@ -250,6 +254,7 @@ module Capybara
250
254
  begin
251
255
  synchronize(query.wait) do
252
256
  result = query.resolve_for(self)
257
+ result.allow_reload! if allow_reload
253
258
  raise Capybara::ExpectationNotMet, result.failure_message unless result.matches_count?
254
259
 
255
260
  result
@@ -60,15 +60,16 @@ module Capybara
60
60
  # @param styles [Hash]
61
61
  # @return [Boolean] If the styles match
62
62
  #
63
- def matches_style?(styles, **options)
64
- make_predicate(options) { assert_matches_style(styles, options) }
63
+ def matches_style?(styles = nil, **options)
64
+ styles, options = options, {} if styles.nil?
65
+ make_predicate(options) { assert_matches_style(styles, **options) }
65
66
  end
66
67
 
67
68
  ##
68
69
  # @deprecated Use {#matches_style?} instead.
69
70
  #
70
- def has_style?(styles, **options)
71
- warn 'DEPRECATED: has_style? is deprecated, please use matches_style?'
71
+ def has_style?(styles = nil, **options)
72
+ Capybara::Helpers.warn "DEPRECATED: has_style? is deprecated, please use matches_style? : #{Capybara::Helpers.filter_backtrace(caller)}"
72
73
  matches_style?(styles, **options)
73
74
  end
74
75
 
@@ -122,9 +123,10 @@ module Capybara
122
123
  # @param styles [Hash]
123
124
  # @raise [Capybara::ExpectationNotMet] If the element doesn't have the specified styles
124
125
  #
125
- def assert_matches_style(styles, **options)
126
- query_args = _set_query_session_options(styles, **options)
127
- query = Capybara::Queries::StyleQuery.new(*query_args)
126
+ def assert_matches_style(styles = nil, **options)
127
+ styles, options = options, {} if styles.nil?
128
+ query_args, query_opts = _set_query_session_options(styles, options)
129
+ query = Capybara::Queries::StyleQuery.new(*query_args, **query_opts)
128
130
  synchronize(query.wait) do
129
131
  raise Capybara::ExpectationNotMet, query.failure_message unless query.resolves_for?(self)
130
132
  end
@@ -134,7 +136,7 @@ module Capybara
134
136
  ##
135
137
  # @deprecated Use {#assert_matches_style} instead.
136
138
  #
137
- def assert_style(styles, **options)
139
+ def assert_style(styles = nil, **options)
138
140
  warn 'assert_style is deprecated, please use assert_matches_style instead'
139
141
  assert_matches_style(styles, **options)
140
142
  end
@@ -201,12 +203,10 @@ module Capybara
201
203
  selector = extract_selector(args)
202
204
  synchronize(wait) do
203
205
  res = args.map do |locator|
204
- begin
205
- assert_selector(selector, locator, options, &optional_filter_block)
206
- break nil
207
- rescue Capybara::ExpectationNotMet => e
208
- e.message
209
- end
206
+ assert_selector(selector, locator, options, &optional_filter_block)
207
+ break nil
208
+ rescue Capybara::ExpectationNotMet => e
209
+ e.message
210
210
  end
211
211
  raise Capybara::ExpectationNotMet, res.join(' or ') if res
212
212
 
@@ -386,7 +386,7 @@ module Capybara
386
386
  #
387
387
  # page.has_field?('Email', type: 'email')
388
388
  #
389
- # Note: 'textarea' and 'select' are valid type values, matching the associated tag names.
389
+ # NOTE: 'textarea' and 'select' are valid type values, matching the associated tag names.
390
390
  #
391
391
  # @param [String] locator The label, name or id of a field to check for
392
392
  # @option options [String, Regexp] :with The text content of the field or a Regexp to match
@@ -672,8 +672,8 @@ module Capybara
672
672
  # @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
673
673
  # @return [true]
674
674
  #
675
- def assert_text(*args)
676
- _verify_text(*args) do |count, query|
675
+ def assert_text(type_or_text, *args, **opts)
676
+ _verify_text(type_or_text, *args, **opts) do |count, query|
677
677
  unless query.matches_count?(count) && (count.positive? || query.expects_none?)
678
678
  raise Capybara::ExpectationNotMet, query.failure_message
679
679
  end
@@ -688,8 +688,8 @@ module Capybara
688
688
  # @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
689
689
  # @return [true]
690
690
  #
691
- def assert_no_text(*args)
692
- _verify_text(*args) do |count, query|
691
+ def assert_no_text(type_or_text, *args, **opts)
692
+ _verify_text(type_or_text, *args, **opts) do |count, query|
693
693
  if query.matches_count?(count) && (count.positive? || query.expects_none?)
694
694
  raise Capybara::ExpectationNotMet, query.negative_failure_message
695
695
  end
@@ -711,7 +711,7 @@ module Capybara
711
711
  # @return [Boolean] Whether it exists
712
712
  #
713
713
  def has_text?(*args, **options)
714
- make_predicate(options) { assert_text(*args, options) }
714
+ make_predicate(options) { assert_text(*args, **options) }
715
715
  end
716
716
  alias_method :has_content?, :has_text?
717
717
 
@@ -723,7 +723,7 @@ module Capybara
723
723
  # @return [Boolean] Whether it doesn't exist
724
724
  #
725
725
  def has_no_text?(*args, **options)
726
- make_predicate(options) { assert_no_text(*args, options) }
726
+ make_predicate(options) { assert_no_text(*args, **options) }
727
727
  end
728
728
  alias_method :has_no_content?, :has_no_text?
729
729
 
@@ -832,8 +832,14 @@ module Capybara
832
832
  end
833
833
 
834
834
  def _verify_selector_result(query_args, optional_filter_block, query_type = Capybara::Queries::SelectorQuery)
835
- query_args = _set_query_session_options(*query_args)
836
- query = query_type.new(*query_args, &optional_filter_block)
835
+ # query_args, query_opts = if query_args[0].is_a? Symbol
836
+ # a,o = _set_query_session_options(*query_args.slice(2..))
837
+ # [query_args.slice(0..1).concat(a), o]
838
+ # else
839
+ # _set_query_session_options(*query_args)
840
+ # end
841
+ query_args, query_opts = _set_query_session_options(*query_args)
842
+ query = query_type.new(*query_args, **query_opts, &optional_filter_block)
837
843
  synchronize(query.wait) do
838
844
  yield query.resolve_for(self), query
839
845
  end
@@ -841,8 +847,8 @@ module Capybara
841
847
  end
842
848
 
843
849
  def _verify_match_result(query_args, optional_filter_block)
844
- query_args = _set_query_session_options(*query_args)
845
- query = Capybara::Queries::MatchQuery.new(*query_args, &optional_filter_block)
850
+ query_args, query_opts = _set_query_session_options(*query_args)
851
+ query = Capybara::Queries::MatchQuery.new(*query_args, **query_opts, &optional_filter_block)
846
852
  synchronize(query.wait) do
847
853
  yield query.resolve_for(parent || session&.document || query_scope)
848
854
  end
@@ -858,9 +864,12 @@ module Capybara
858
864
  true
859
865
  end
860
866
 
861
- def _set_query_session_options(*query_args, **query_options)
867
+ def _set_query_session_options(*query_args)
868
+ query_args, query_options = query_args.dup, {}
869
+ # query_options = query_args.pop if query_options.empty? && query_args.last.is_a?(Hash)
870
+ query_options = query_args.pop if query_args.last.is_a?(Hash)
862
871
  query_options[:session_options] = session_options
863
- query_args.push(query_options)
872
+ [query_args, query_options]
864
873
  end
865
874
 
866
875
  def make_predicate(options)
@@ -100,7 +100,7 @@ module Capybara
100
100
  # @param [Boolean] check_ancestors Whether to inherit visibility from ancestors
101
101
  # @return [Boolean] Whether the element is visible
102
102
  #
103
- def visible?(check_ancestors = true)
103
+ def visible?(check_ancestors = true) # rubocop:disable Style/OptionalBooleanParameter
104
104
  return false if (tag_name == 'input') && (native[:type] == 'hidden')
105
105
  return false if tag_name == 'template'
106
106
 
@@ -148,11 +148,15 @@ module Capybara
148
148
  native.has_attribute?('multiple')
149
149
  end
150
150
 
151
+ def readonly?
152
+ native.has_attribute?('readonly')
153
+ end
154
+
151
155
  def synchronize(_seconds = nil)
152
156
  yield # simple nodes don't need to wait
153
157
  end
154
158
 
155
- def allow_reload!
159
+ def allow_reload!(*)
156
160
  # no op
157
161
  end
158
162
 
@@ -16,7 +16,7 @@ module Capybara
16
16
  end
17
17
  end
18
18
 
19
- def description(applied = false)
19
+ def description(applied = false) # rubocop:disable Style/OptionalBooleanParameter
20
20
  child_query = @child_node&.instance_variable_get(:@query)
21
21
  desc = super
22
22
  desc += " that is an ancestor of #{child_query.description}" if child_query
@@ -79,7 +79,8 @@ module Capybara
79
79
  if count
80
80
  message << " #{occurrences count}"
81
81
  elsif between
82
- message << " between #{between.first} and #{between.end ? between.last : 'infinite'} times"
82
+ message << " between #{between.begin ? between.first : 1} and" \
83
+ " #{between.end ? between.last : 'infinite'} times"
83
84
  elsif maximum
84
85
  message << " at most #{occurrences maximum}"
85
86
  elsif minimum
@@ -6,26 +6,30 @@ module Capybara
6
6
  # @api private
7
7
  module Queries
8
8
  class CurrentPathQuery < BaseQuery
9
- def initialize(expected_path, **options)
9
+ def initialize(expected_path, **options, &optional_filter_block)
10
10
  super(options)
11
11
  @expected_path = expected_path
12
12
  @options = {
13
13
  url: !@expected_path.is_a?(Regexp) && !::Addressable::URI.parse(@expected_path || '').hostname.nil?,
14
14
  ignore_query: false
15
15
  }.merge(options)
16
+ @filter_block = optional_filter_block
16
17
  assert_valid_keys
17
18
  end
18
19
 
19
20
  def resolves_for?(session)
20
21
  uri = ::Addressable::URI.parse(session.current_url)
21
- uri&.query = nil if options[:ignore_query]
22
- @actual_path = options[:url] ? uri&.to_s : uri&.request_uri
22
+ @actual_path = (options[:ignore_query] ? uri&.omit(:query) : uri).yield_self do |u|
23
+ options[:url] ? u&.to_s : u&.request_uri
24
+ end
23
25
 
24
- if @expected_path.is_a? Regexp
26
+ res = if @expected_path.is_a? Regexp
25
27
  @actual_path.to_s.match?(@expected_path)
26
28
  else
27
29
  ::Addressable::URI.parse(@expected_path) == ::Addressable::URI.parse(@actual_path)
28
30
  end
31
+
32
+ res && matches_filter_block?(uri)
29
33
  end
30
34
 
31
35
  def failure_message
@@ -38,6 +42,12 @@ module Capybara
38
42
 
39
43
  private
40
44
 
45
+ def matches_filter_block?(url)
46
+ return true unless @filter_block
47
+
48
+ @filter_block.call(url)
49
+ end
50
+
41
51
  def failure_message_helper(negated = '')
42
52
  verb = @expected_path.is_a?(Regexp) ? 'match' : 'equal'
43
53
  "expected #{@actual_path.inspect}#{negated} to #{verb} #{@expected_path.inspect}"
@@ -6,6 +6,7 @@ module Capybara
6
6
  module Queries
7
7
  class SelectorQuery < Queries::BaseQuery
8
8
  attr_reader :expression, :selector, :locator, :options
9
+
9
10
  SPATIAL_KEYS = %i[above below left_of right_of near].freeze
10
11
  VALID_KEYS = SPATIAL_KEYS + COUNT_KEYS +
11
12
  %i[text id class style visible obscured exact exact_text normalize_ws match wait filter_set]
@@ -14,6 +15,7 @@ module Capybara
14
15
  def initialize(*args,
15
16
  session_options:,
16
17
  enable_aria_label: session_options.enable_aria_label,
18
+ enable_aria_role: session_options.enable_aria_role,
17
19
  test_id: session_options.test_id,
18
20
  selector_format: nil,
19
21
  order: nil,
@@ -30,7 +32,11 @@ module Capybara
30
32
 
31
33
  @selector = Selector.new(
32
34
  find_selector(args[0].is_a?(Symbol) ? args.shift : args[0]),
33
- config: { enable_aria_label: enable_aria_label, test_id: test_id },
35
+ config: {
36
+ enable_aria_label: enable_aria_label,
37
+ enable_aria_role: enable_aria_role,
38
+ test_id: test_id
39
+ },
34
40
  format: selector_format
35
41
  )
36
42
 
@@ -49,7 +55,7 @@ module Capybara
49
55
  def name; selector.name; end
50
56
  def label; selector.label || selector.name; end
51
57
 
52
- def description(only_applied = false)
58
+ def description(only_applied = false) # rubocop:disable Style/OptionalBooleanParameter
53
59
  desc = +''
54
60
  show_for = show_for_stage(only_applied)
55
61
 
@@ -89,11 +95,9 @@ module Capybara
89
95
  desc << ' that also matches the custom filter block' if @filter_block && show_for[:node]
90
96
 
91
97
  desc << " within #{@resolved_node.inspect}" if describe_within?
92
- if locator.is_a?(String) && locator.start_with?('#', './/', '//')
93
- unless selector.raw_locator?
94
- desc << "\nNote: It appears you may be passing a CSS selector or XPath expression rather than a locator. " \
95
- "Please see the documentation for acceptable locator values.\n\n"
96
- end
98
+ if locator.is_a?(String) && locator.start_with?('#', './/', '//') && !selector.raw_locator?
99
+ desc << "\nNote: It appears you may be passing a CSS selector or XPath expression rather than a locator. " \
100
+ "Please see the documentation for acceptable locator values.\n\n"
97
101
  end
98
102
  desc
99
103
  end
@@ -233,17 +237,18 @@ module Capybara
233
237
  hints[:styles] = options[:style] if use_default_style_filter?
234
238
  hints[:position] = true if use_spatial_filter?
235
239
 
236
- if selector_format == :css
237
- if node.method(:find_css).arity != 1
238
- node.find_css(css, **hints)
239
- else
240
+ case selector_format
241
+ when :css
242
+ if node.method(:find_css).arity == 1
240
243
  node.find_css(css)
241
- end
242
- elsif selector_format == :xpath
243
- if node.method(:find_xpath).arity != 1
244
- node.find_xpath(xpath(exact), **hints)
245
244
  else
245
+ node.find_css(css, **hints)
246
+ end
247
+ when :xpath
248
+ if node.method(:find_xpath).arity == 1
246
249
  node.find_xpath(xpath(exact))
250
+ else
251
+ node.find_xpath(xpath(exact), **hints)
247
252
  end
248
253
  else
249
254
  raise ArgumentError, "Unknown format: #{selector_format}"
@@ -477,9 +482,25 @@ module Capybara
477
482
  end
478
483
 
479
484
  def matches_class_filter?(node)
480
- return true unless use_default_class_filter? && options[:class].is_a?(Regexp)
485
+ return true unless use_default_class_filter? && need_to_process_classes?
481
486
 
482
- options[:class].match? node[:class]
487
+ if options[:class].is_a? Regexp
488
+ options[:class].match? node[:class]
489
+ else
490
+ classes = (node[:class] || '').split
491
+ options[:class].select { |c| c.is_a? Regexp }.all? do |r|
492
+ classes.any? { |cls| r.match? cls }
493
+ end
494
+ end
495
+ end
496
+
497
+ def need_to_process_classes?
498
+ case options[:class]
499
+ when Regexp then true
500
+ when Array then options[:class].any?(Regexp)
501
+ else
502
+ false
503
+ end
483
504
  end
484
505
 
485
506
  def matches_style_filter?(node)
@@ -575,6 +596,7 @@ module Capybara
575
596
 
576
597
  class Rectangle
577
598
  attr_reader :top, :bottom, :left, :right
599
+
578
600
  def initialize(position)
579
601
  # rubocop:disable Style/RescueModifier
580
602
  @top = position['top'] rescue position['y']
@@ -645,7 +667,7 @@ module Capybara
645
667
 
646
668
  d = u.dot w
647
669
  e = v.dot w
648
- cap_d = (a * c) - (b * b)
670
+ cap_d = (a * c) - (b**2)
649
671
  sD = tD = cap_d
650
672
 
651
673
  # compute the line parameters of the two closest points
@@ -15,7 +15,7 @@ module Capybara
15
15
  end
16
16
  end
17
17
 
18
- def description(applied = false)
18
+ def description(applied = false) # rubocop:disable Style/OptionalBooleanParameter
19
19
  desc = super
20
20
  sibling_query = @sibling_node&.instance_variable_get(:@query)
21
21
  desc += " that is a sibling of #{sibling_query.description}" if sibling_query
@@ -34,7 +34,7 @@ module Capybara
34
34
  private
35
35
 
36
36
  def stringify_keys(hsh)
37
- hsh.each_with_object({}) { |(k, v), str_keys| str_keys[k.to_s] = v }
37
+ hsh.transform_keys(&:to_s)
38
38
  end
39
39
 
40
40
  def valid_keys
@@ -6,13 +6,15 @@ module Capybara
6
6
  class TextQuery < BaseQuery
7
7
  def initialize(type = nil, expected_text, session_options:, **options) # rubocop:disable Style/OptionalArguments
8
8
  @type = type.nil? ? default_type : type
9
+ raise ArgumentError, "#{@type} is not a valid type for a text query" unless valid_types.include?(@type)
10
+
9
11
  @options = options
10
12
  super(@options)
11
13
  self.session_options = session_options
12
14
 
13
15
  if expected_text.nil? && !exact?
14
16
  warn 'Checking for expected text of nil is confusing and/or pointless since it will always match. '\
15
- 'Please specify a string or regexp instead.'
17
+ "Please specify a string or regexp instead. #{Capybara::Helpers.filter_backtrace(caller)}"
16
18
  end
17
19
 
18
20
  @expected_text = expected_text.is_a?(Regexp) ? expected_text : expected_text.to_s
@@ -89,6 +91,10 @@ module Capybara
89
91
  COUNT_KEYS + %i[wait exact normalize_ws]
90
92
  end
91
93
 
94
+ def valid_types
95
+ %i[all visible]
96
+ end
97
+
92
98
  def check_visible_text?
93
99
  @type == :visible
94
100
  end