capybara 3.33.0 → 3.34.0

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