capybara 3.33.0 → 3.34.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.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +44 -15
  3. data/README.md +0 -2
  4. data/lib/capybara.rb +1 -1
  5. data/lib/capybara/config.rb +4 -6
  6. data/lib/capybara/driver/base.rb +4 -0
  7. data/lib/capybara/helpers.rb +25 -1
  8. data/lib/capybara/minitest.rb +2 -2
  9. data/lib/capybara/minitest/spec.rb +14 -11
  10. data/lib/capybara/node/actions.rb +1 -2
  11. data/lib/capybara/node/element.rb +1 -5
  12. data/lib/capybara/node/finders.rb +7 -6
  13. data/lib/capybara/node/matchers.rb +7 -5
  14. data/lib/capybara/node/simple.rb +5 -1
  15. data/lib/capybara/queries/ancestor_query.rb +1 -1
  16. data/lib/capybara/queries/current_path_query.rb +14 -4
  17. data/lib/capybara/queries/selector_query.rb +8 -9
  18. data/lib/capybara/queries/sibling_query.rb +1 -1
  19. data/lib/capybara/queries/text_query.rb +2 -2
  20. data/lib/capybara/rack_test/browser.rb +7 -3
  21. data/lib/capybara/rack_test/driver.rb +1 -0
  22. data/lib/capybara/rack_test/form.rb +1 -1
  23. data/lib/capybara/rack_test/node.rb +1 -1
  24. data/lib/capybara/registration_container.rb +3 -3
  25. data/lib/capybara/registrations/patches/puma_ssl.rb +3 -1
  26. data/lib/capybara/registrations/servers.rb +1 -0
  27. data/lib/capybara/result.rb +3 -7
  28. data/lib/capybara/rspec.rb +2 -0
  29. data/lib/capybara/rspec/matcher_proxies.rb +1 -1
  30. data/lib/capybara/rspec/matchers.rb +7 -6
  31. data/lib/capybara/rspec/matchers/have_current_path.rb +2 -2
  32. data/lib/capybara/rspec/matchers/match_style.rb +5 -0
  33. data/lib/capybara/selector/definition.rb +6 -5
  34. data/lib/capybara/selector/definition/button.rb +7 -5
  35. data/lib/capybara/selector/definition/css.rb +1 -1
  36. data/lib/capybara/selector/definition/datalist_input.rb +1 -1
  37. data/lib/capybara/selector/definition/element.rb +2 -1
  38. data/lib/capybara/selector/definition/label.rb +1 -1
  39. data/lib/capybara/selector/definition/select.rb +1 -1
  40. data/lib/capybara/selector/definition/table_row.rb +1 -1
  41. data/lib/capybara/selector/filter_set.rb +2 -2
  42. data/lib/capybara/selector/selector.rb +5 -1
  43. data/lib/capybara/selenium/driver.rb +29 -3
  44. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +3 -3
  45. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +3 -3
  46. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +1 -1
  47. data/lib/capybara/selenium/extensions/find.rb +3 -3
  48. data/lib/capybara/selenium/extensions/scroll.rb +8 -10
  49. data/lib/capybara/selenium/node.rb +6 -3
  50. data/lib/capybara/selenium/nodes/chrome_node.rb +20 -2
  51. data/lib/capybara/selenium/nodes/safari_node.rb +1 -1
  52. data/lib/capybara/selenium/patches/atoms.rb +4 -4
  53. data/lib/capybara/selenium/patches/logs.rb +4 -4
  54. data/lib/capybara/server/animation_disabler.rb +3 -2
  55. data/lib/capybara/server/middleware.rb +4 -2
  56. data/lib/capybara/session.rb +20 -11
  57. data/lib/capybara/session/matchers.rb +11 -11
  58. data/lib/capybara/spec/public/test.js +6 -1
  59. data/lib/capybara/spec/session/accept_alert_spec.rb +1 -1
  60. data/lib/capybara/spec/session/check_spec.rb +6 -0
  61. data/lib/capybara/spec/session/current_url_spec.rb +11 -1
  62. data/lib/capybara/spec/session/has_button_spec.rb +2 -0
  63. data/lib/capybara/spec/session/has_css_spec.rb +2 -1
  64. data/lib/capybara/spec/session/has_current_path_spec.rb +13 -0
  65. data/lib/capybara/spec/session/has_text_spec.rb +0 -11
  66. data/lib/capybara/spec/session/matches_style_spec.rb +2 -2
  67. data/lib/capybara/spec/session/node_spec.rb +22 -2
  68. data/lib/capybara/spec/session/refresh_spec.rb +1 -0
  69. data/lib/capybara/spec/session/save_page_spec.rb +4 -4
  70. data/lib/capybara/spec/spec_helper.rb +11 -12
  71. data/lib/capybara/spec/test_app.rb +8 -3
  72. data/lib/capybara/spec/views/form.erb +18 -0
  73. data/lib/capybara/spec/views/with_animation.erb +8 -0
  74. data/lib/capybara/spec/views/with_js.erb +1 -0
  75. data/lib/capybara/spec/views/with_sortable_js.erb +1 -1
  76. data/lib/capybara/version.rb +1 -1
  77. data/lib/capybara/window.rb +3 -7
  78. data/spec/basic_node_spec.rb +9 -8
  79. data/spec/dsl_spec.rb +1 -1
  80. data/spec/fixtures/selenium_driver_rspec_success.rb +1 -1
  81. data/spec/minitest_spec.rb +2 -1
  82. data/spec/rack_test_spec.rb +15 -5
  83. data/spec/rspec/features_spec.rb +3 -1
  84. data/spec/rspec/scenarios_spec.rb +4 -0
  85. data/spec/rspec/shared_spec_matchers.rb +2 -2
  86. data/spec/rspec_spec.rb +4 -0
  87. data/spec/selector_spec.rb +1 -0
  88. data/spec/server_spec.rb +15 -0
  89. data/spec/shared_selenium_session.rb +60 -1
  90. metadata +22 -23
  91. data/lib/capybara/spec/session/source_spec.rb +0 -0
@@ -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}"
@@ -55,7 +55,7 @@ module Capybara
55
55
  def name; selector.name; end
56
56
  def label; selector.label || selector.name; end
57
57
 
58
- def description(only_applied = false)
58
+ def description(only_applied = false) # rubocop:disable Style/OptionalBooleanParameter
59
59
  desc = +''
60
60
  show_for = show_for_stage(only_applied)
61
61
 
@@ -95,11 +95,9 @@ module Capybara
95
95
  desc << ' that also matches the custom filter block' if @filter_block && show_for[:node]
96
96
 
97
97
  desc << " within #{@resolved_node.inspect}" if describe_within?
98
- if locator.is_a?(String) && locator.start_with?('#', './/', '//')
99
- unless selector.raw_locator?
100
- desc << "\nNote: It appears you may be passing a CSS selector or XPath expression rather than a locator. " \
101
- "Please see the documentation for acceptable locator values.\n\n"
102
- 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"
103
101
  end
104
102
  desc
105
103
  end
@@ -239,13 +237,14 @@ module Capybara
239
237
  hints[:styles] = options[:style] if use_default_style_filter?
240
238
  hints[:position] = true if use_spatial_filter?
241
239
 
242
- if selector_format == :css
240
+ case selector_format
241
+ when :css
243
242
  if node.method(:find_css).arity != 1
244
243
  node.find_css(css, **hints)
245
244
  else
246
245
  node.find_css(css)
247
246
  end
248
- elsif selector_format == :xpath
247
+ when :xpath
249
248
  if node.method(:find_xpath).arity != 1
250
249
  node.find_xpath(xpath(exact), **hints)
251
250
  else
@@ -652,7 +651,7 @@ module Capybara
652
651
 
653
652
  d = u.dot w
654
653
  e = v.dot w
655
- cap_d = (a * c) - (b * b)
654
+ cap_d = (a * c) - (b**2)
656
655
  sD = tD = cap_d
657
656
 
658
657
  # 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
@@ -6,7 +6,7 @@ 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)
9
+ raise ArgumentError, "#{@type} is not a valid type for a text query" unless valid_types.include?(@type)
10
10
 
11
11
  @options = options
12
12
  super(@options)
@@ -14,7 +14,7 @@ module Capybara
14
14
 
15
15
  if expected_text.nil? && !exact?
16
16
  warn 'Checking for expected text of nil is confusing and/or pointless since it will always match. '\
17
- 'Please specify a string or regexp instead.'
17
+ "Please specify a string or regexp instead. #{Capybara::Helpers.filter_backtrace(caller)}"
18
18
  end
19
19
 
20
20
  @expected_text = expected_text.is_a?(Regexp) ? expected_text : expected_text.to_s
@@ -8,6 +8,7 @@ class Capybara::RackTest::Browser
8
8
 
9
9
  def initialize(driver)
10
10
  @driver = driver
11
+ @current_fragment = nil
11
12
  end
12
13
 
13
14
  def app
@@ -31,7 +32,7 @@ class Capybara::RackTest::Browser
31
32
  def submit(method, path, attributes)
32
33
  path = request_path if path.nil? || path.empty?
33
34
  uri = build_uri(path)
34
- uri.query = '' if method&.to_s&.downcase == 'get'
35
+ uri.query = '' if method.to_s.casecmp('get').zero?
35
36
  process_and_follow_redirects(method, uri.to_s, attributes, 'HTTP_REFERER' => current_url)
36
37
  end
37
38
 
@@ -42,6 +43,7 @@ class Capybara::RackTest::Browser
42
43
  end
43
44
 
44
45
  def process_and_follow_redirects(method, path, attributes = {}, env = {})
46
+ @current_fragment = build_uri(path).fragment
45
47
  process(method, path, attributes, env)
46
48
 
47
49
  return unless driver.follow_redirects?
@@ -65,7 +67,7 @@ class Capybara::RackTest::Browser
65
67
  method = method.downcase
66
68
  new_uri = build_uri(path)
67
69
  @current_scheme, @current_host, @current_port = new_uri.select(:scheme, :host, :port)
68
-
70
+ @current_fragment = new_uri.fragment || @current_fragment
69
71
  reset_cache!
70
72
  send(method, new_uri.to_s, attributes, env.merge(options[:headers] || {}))
71
73
  end
@@ -83,7 +85,9 @@ class Capybara::RackTest::Browser
83
85
  end
84
86
 
85
87
  def current_url
86
- last_request.url
88
+ uri = build_uri(last_request.url)
89
+ uri.fragment = @current_fragment if @current_fragment
90
+ uri.to_s
87
91
  rescue Rack::Test::Error
88
92
  ''
89
93
  end
@@ -17,6 +17,7 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
17
17
  def initialize(app, **options)
18
18
  raise ArgumentError, 'rack-test requires a rack application, but none was given' unless app
19
19
 
20
+ super()
20
21
  @app = app
21
22
  @options = DEFAULT_OPTIONS.merge(options)
22
23
  end
@@ -6,7 +6,7 @@ class Capybara::RackTest::Form < Capybara::RackTest::Node
6
6
  # That check should be based solely on the form element's 'enctype' attribute value,
7
7
  # which should probably be provided to Rack::Test in its non-GET request methods.
8
8
  class NilUploadedFile < Rack::Test::UploadedFile
9
- def initialize
9
+ def initialize # rubocop:disable Lint/MissingSuper
10
10
  @empty_file = Tempfile.new('nil_uploaded_file')
11
11
  @empty_file.close
12
12
  end
@@ -15,7 +15,7 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
15
15
  end
16
16
 
17
17
  def visible_text
18
- displayed_text.gsub(/\ +/, ' ')
18
+ displayed_text.squeeze(' ')
19
19
  .gsub(/[\ \n]*\n[\ \n]*/, "\n")
20
20
  .gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
21
21
  .gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
@@ -12,13 +12,13 @@ module Capybara
12
12
  end
13
13
 
14
14
  def []=(name, value)
15
- warn 'DEPRECATED: Directly setting drivers/servers is deprecated, please use Capybara.register_driver/register_server instead'
15
+ Capybara::Helpers.warn 'DEPRECATED: Directly setting drivers/servers is deprecated, please use Capybara.register_driver/register_server instead'
16
16
  @registered[name] = value
17
17
  end
18
18
 
19
19
  def method_missing(method_name, *args, **options, &block)
20
20
  if @registered.respond_to?(method_name)
21
- warn "DEPRECATED: Calling '#{method_name}' on the drivers/servers container is deprecated without replacement"
21
+ Capybara::Helpers.warn "DEPRECATED: Calling '#{method_name}' on the drivers/servers container is deprecated without replacement"
22
22
  # RUBY 2.6 will send an empty hash rather than nothing with **options so fix that
23
23
  return @registered.public_send(method_name, *args, &block) if options.empty?
24
24
 
@@ -27,7 +27,7 @@ module Capybara
27
27
  super
28
28
  end
29
29
 
30
- def respond_to_missing?(method_name, include_private = false)
30
+ def respond_to_missing?(method_name, include_all)
31
31
  @registered.respond_to?(method_name) || super
32
32
  end
33
33
 
@@ -4,12 +4,14 @@ module Puma
4
4
  module MiniSSL
5
5
  class Socket
6
6
  def read_nonblock(size, *_)
7
+ wait_states = %i[wait_readable wait_writable]
8
+
7
9
  loop do
8
10
  output = engine_read_all
9
11
  return output if output
10
12
 
11
13
  data = @socket.read_nonblock(size, exception: false)
12
- raise IO::EAGAINWaitReadable if %i[wait_readable wait_writable].include? data
14
+ raise IO::EAGAINWaitReadable if wait_states.include? data
13
15
  return nil if data.nil?
14
16
 
15
17
  @engine.inject(data)
@@ -28,6 +28,7 @@ Capybara.register_server :puma do |app, port, host, **options|
28
28
  options = default_options.merge(options)
29
29
 
30
30
  conf = Rack::Handler::Puma.config(app, options)
31
+ conf.clamp
31
32
  events = conf.options[:Silent] ? ::Puma::Events.strings : ::Puma::Events.stdio
32
33
 
33
34
  puma_ver = Gem::Version.new(Puma::Const::PUMA_VERSION)
@@ -39,7 +39,7 @@ module Capybara
39
39
  alias index find_index
40
40
 
41
41
  def each(&block)
42
- return enum_for(:each) unless block_given?
42
+ return enum_for(:each) unless block
43
43
 
44
44
  @result_cache.each(&block)
45
45
  loop do
@@ -91,13 +91,9 @@ module Capybara
91
91
  return load_up_to(count + 1) <=> count
92
92
  end
93
93
 
94
- if min && (min = Integer(min))
95
- return -1 if load_up_to(min) < min
96
- end
94
+ return -1 if min && (min = Integer(min)) && (load_up_to(min) < min)
97
95
 
98
- if max && (max = Integer(max))
99
- return 1 if load_up_to(max + 1) > max
100
- end
96
+ return 1 if max && (max = Integer(max)) && (load_up_to(max + 1) > max)
101
97
 
102
98
  if between
103
99
  min, max = (between.begin && between.min) || 1, between.end
@@ -9,6 +9,8 @@ require 'capybara/rspec/matcher_proxies'
9
9
  RSpec.configure do |config|
10
10
  config.include Capybara::DSL, type: :feature
11
11
  config.include Capybara::RSpecMatchers, type: :feature
12
+ config.include Capybara::DSL, type: :system
13
+ config.include Capybara::RSpecMatchers, type: :system
12
14
  config.include Capybara::RSpecMatchers, type: :view
13
15
 
14
16
  # The before and after blocks must run instantaneously, because Capybara
@@ -11,7 +11,7 @@ module Capybara
11
11
  end
12
12
 
13
13
  def within(*args, **kwargs, &block)
14
- if block_given?
14
+ if block
15
15
  within_element(*args, **kwargs, &block)
16
16
  else
17
17
  be_within(*args)
@@ -94,7 +94,7 @@ module Capybara
94
94
  # @see Capybara::Node::Matchers#has_button?
95
95
 
96
96
  # @!method have_field(locator = nil, **options, &optional_filter_block)
97
- # RSpec matcher for links.
97
+ # RSpec matcher for form fields.
98
98
  #
99
99
  # @see Capybara::Node::Matchers#has_field?
100
100
 
@@ -139,22 +139,23 @@ module Capybara
139
139
  # RSpec matcher for the current path.
140
140
  #
141
141
  # @see Capybara::SessionMatchers#assert_current_path
142
- def have_current_path(path, **options)
143
- Matchers::HaveCurrentPath.new(path, **options)
142
+ def have_current_path(path, **options, &optional_filter_block)
143
+ Matchers::HaveCurrentPath.new(path, **options, &optional_filter_block)
144
144
  end
145
145
 
146
146
  # RSpec matcher for element style.
147
147
  #
148
148
  # @see Capybara::Node::Matchers#matches_style?
149
- def match_style(styles, **options)
149
+ def match_style(styles = nil, **options)
150
+ styles, options = options, {} if styles.nil?
150
151
  Matchers::MatchStyle.new(styles, **options)
151
152
  end
152
153
 
153
154
  ##
154
155
  # @deprecated
155
156
  #
156
- def have_style(styles, **options)
157
- warn 'DEPRECATED: have_style is deprecated, please use match_style'
157
+ def have_style(styles = nil, **options)
158
+ Capybara::Helpers.warn "DEPRECATED: have_style is deprecated, please use match_style : #{Capybara::Helpers.filter_backtrace(caller)}"
158
159
  match_style(styles, **options)
159
160
  end
160
161
 
@@ -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(current_path, **@kw_args)
10
+ el.assert_current_path(current_path, **@kw_args, &@filter_block)
11
11
  end
12
12
 
13
13
  def element_does_not_match?(el)
14
- el.assert_no_current_path(current_path, **@kw_args)
14
+ el.assert_no_current_path(current_path, **@kw_args, &@filter_block)
15
15
  end
16
16
 
17
17
  def description
@@ -6,6 +6,11 @@ module Capybara
6
6
  module RSpecMatchers
7
7
  module Matchers
8
8
  class MatchStyle < WrappedElementMatcher
9
+ def initialize(styles = nil, **kw_args, &filter_block)
10
+ styles, kw_args = kw_args, {} if styles.nil?
11
+ super(styles, **kw_args, &filter_block)
12
+ end
13
+
9
14
  def element_matches?(el)
10
15
  el.assert_matches_style(*@args, **@kw_args)
11
16
  end
@@ -15,7 +15,7 @@ module Capybara
15
15
 
16
16
  def initialize(name, locator_type: nil, raw_locator: false, supports_exact: nil, &block)
17
17
  @name = name
18
- @filter_set = Capybara::Selector::FilterSet.add(name) {}
18
+ @filter_set = Capybara::Selector::FilterSet.add(name)
19
19
  @match = nil
20
20
  @label = nil
21
21
  @failure_message = nil
@@ -83,7 +83,7 @@ module Capybara
83
83
  # Automatic selector detection
84
84
  #
85
85
  # @yield [locator] This block takes the passed in locator string and returns whether or not it matches the selector
86
- # @yieldparam [String], locator The locator string used to determin if it matches the selector
86
+ # @yieldparam [String], locator The locator string used to determine if it matches the selector
87
87
  # @yieldreturn [Boolean] Whether this selector matches the locator string
88
88
  # @return [#call] The block that will be used to detect selector match
89
89
  #
@@ -178,7 +178,7 @@ module Capybara
178
178
  def_delegator :@filter_set, :describe
179
179
 
180
180
  def describe_expression_filters(&block)
181
- if block_given?
181
+ if block
182
182
  describe(:expression_filters, &block)
183
183
  else
184
184
  describe(:expression_filters) do |**options|
@@ -215,7 +215,7 @@ module Capybara
215
215
  end
216
216
 
217
217
  def default_visibility(fallback = Capybara.ignore_hidden_elements, options = {})
218
- vis = if @default_visibility&.respond_to?(:call)
218
+ vis = if @default_visibility.respond_to?(:call)
219
219
  @default_visibility.call(options)
220
220
  else
221
221
  @default_visibility
@@ -259,7 +259,8 @@ module Capybara
259
259
  end
260
260
 
261
261
  def parameter_names(block)
262
- block.parameters.select { |(type, _name)| %i[key keyreq].include? type }.map { |(_type, name)| name }
262
+ key_types = %i[key keyreq]
263
+ block.parameters.select { |(type, _name)| key_types.include? type }.map { |(_type, name)| name }
263
264
  end
264
265
 
265
266
  def expression(type, allowed_filters, &block)
@@ -12,23 +12,25 @@ Capybara.add_selector(:button, locator_type: [String, Symbol]) do
12
12
  locator_matchers = XPath.attr(:id).equals(locator) |
13
13
  XPath.attr(:name).equals(locator) |
14
14
  XPath.attr(:value).is(locator) |
15
- XPath.attr(:title).is(locator)
15
+ XPath.attr(:title).is(locator) |
16
+ (XPath.attr(:id) == XPath.anywhere(:label)[XPath.string.n.is(locator)].attr(:for))
16
17
  locator_matchers |= XPath.attr(:'aria-label').is(locator) if enable_aria_label
17
18
  locator_matchers |= XPath.attr(test_id) == locator if test_id
18
19
 
19
- input_btn_xpath = input_btn_xpath[locator_matchers]
20
+ input_btn_xpath = input_btn_xpath[locator_matchers] + locate_label(locator).descendant(input_btn_xpath)
20
21
 
21
22
  btn_xpath = btn_xpath[locator_matchers |
22
23
  XPath.string.n.is(locator) |
23
- XPath.descendant(:img)[XPath.attr(:alt).is(locator)]]
24
+ XPath.descendant(:img)[XPath.attr(:alt).is(locator)]
25
+ ] + locate_label(locator).descendant(btn_xpath)
24
26
 
25
27
  alt_matches = XPath.attr(:alt).is(locator)
26
28
  alt_matches |= XPath.attr(:'aria-label').is(locator) if enable_aria_label
27
- image_btn_xpath = image_btn_xpath[alt_matches]
29
+ image_btn_xpath = image_btn_xpath[alt_matches] + locate_label(locator).descendant(image_btn_xpath)
28
30
  end
29
31
 
30
32
  %i[value title type].inject(input_btn_xpath.union(btn_xpath).union(image_btn_xpath)) do |memo, ef|
31
- memo[find_by_attr(ef, options[ef])]
33
+ memo.where(find_by_attr(ef, options[ef]))
32
34
  end
33
35
  end
34
36
 
@@ -3,7 +3,7 @@
3
3
  Capybara.add_selector(:css, locator_type: [String, Symbol], raw_locator: true) do
4
4
  css do |css|
5
5
  if css.is_a? Symbol
6
- warn "DEPRECATED: Passing a symbol (#{css.inspect}) as the CSS locator is deprecated - please pass a string instead."
6
+ Capybara::Helpers.warn "DEPRECATED: Passing a symbol (#{css.inspect}) as the CSS locator is deprecated - please pass a string instead : #{Capybara::Helpers.filter_backtrace(caller)}"
7
7
  end
8
8
  css
9
9
  end
@@ -19,7 +19,7 @@ Capybara.add_selector(:datalist_input, locator_type: [String, Symbol]) do
19
19
 
20
20
  expression_filter(:with_options) do |expr, options|
21
21
  options.inject(expr) do |xpath, option|
22
- xpath[XPath.attr(:list) == XPath.anywhere(:datalist)[expression_for(:datalist_option, option)].attr(:id)]
22
+ xpath.where(XPath.attr(:list) == XPath.anywhere(:datalist)[expression_for(:datalist_option, option)].attr(:id))
23
23
  end
24
24
  end
25
25