capybara 3.35.3 → 3.37.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +57 -1
  3. data/README.md +5 -1
  4. data/lib/capybara/config.rb +16 -4
  5. data/lib/capybara/driver/base.rb +4 -0
  6. data/lib/capybara/driver/node.rb +5 -1
  7. data/lib/capybara/dsl.rb +4 -10
  8. data/lib/capybara/helpers.rb +3 -12
  9. data/lib/capybara/minitest/spec.rb +2 -2
  10. data/lib/capybara/node/actions.rb +10 -5
  11. data/lib/capybara/node/document.rb +2 -2
  12. data/lib/capybara/node/element.rb +13 -2
  13. data/lib/capybara/node/finders.rb +9 -2
  14. data/lib/capybara/node/simple.rb +5 -1
  15. data/lib/capybara/queries/active_element_query.rb +18 -0
  16. data/lib/capybara/queries/ancestor_query.rb +2 -1
  17. data/lib/capybara/queries/current_path_query.rb +1 -1
  18. data/lib/capybara/queries/selector_query.rb +34 -8
  19. data/lib/capybara/queries/sibling_query.rb +2 -1
  20. data/lib/capybara/rack_test/browser.rb +56 -7
  21. data/lib/capybara/rack_test/driver.rb +4 -4
  22. data/lib/capybara/rack_test/node.rb +10 -7
  23. data/lib/capybara/registration_container.rb +0 -3
  24. data/lib/capybara/registrations/drivers.rb +3 -3
  25. data/lib/capybara/rspec/matcher_proxies.rb +3 -3
  26. data/lib/capybara/rspec/matchers/have_selector.rb +5 -5
  27. data/lib/capybara/rspec/matchers.rb +14 -14
  28. data/lib/capybara/selector/builders/css_builder.rb +1 -1
  29. data/lib/capybara/selector/builders/xpath_builder.rb +1 -1
  30. data/lib/capybara/selector/css.rb +1 -1
  31. data/lib/capybara/selector/definition/button.rb +9 -4
  32. data/lib/capybara/selector/definition/checkbox.rb +1 -1
  33. data/lib/capybara/selector/definition/file_field.rb +1 -1
  34. data/lib/capybara/selector/definition/fillable_field.rb +1 -1
  35. data/lib/capybara/selector/definition/radio_button.rb +1 -1
  36. data/lib/capybara/selector/definition.rb +3 -1
  37. data/lib/capybara/selector/filter_set.rb +4 -6
  38. data/lib/capybara/selector/selector.rb +5 -1
  39. data/lib/capybara/selector.rb +1 -0
  40. data/lib/capybara/selenium/driver.rb +25 -11
  41. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +1 -1
  42. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +1 -1
  43. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +1 -1
  44. data/lib/capybara/selenium/node.rb +22 -8
  45. data/lib/capybara/selenium/nodes/chrome_node.rb +1 -1
  46. data/lib/capybara/selenium/nodes/edge_node.rb +1 -1
  47. data/lib/capybara/selenium/nodes/firefox_node.rb +1 -1
  48. data/lib/capybara/selenium/nodes/safari_node.rb +2 -2
  49. data/lib/capybara/server/animation_disabler.rb +35 -17
  50. data/lib/capybara/session/config.rb +1 -1
  51. data/lib/capybara/session.rb +20 -23
  52. data/lib/capybara/spec/session/active_element_spec.rb +31 -0
  53. data/lib/capybara/spec/session/all_spec.rb +9 -13
  54. data/lib/capybara/spec/session/assert_text_spec.rb +17 -17
  55. data/lib/capybara/spec/session/check_spec.rb +9 -0
  56. data/lib/capybara/spec/session/choose_spec.rb +6 -0
  57. data/lib/capybara/spec/session/find_spec.rb +6 -0
  58. data/lib/capybara/spec/session/has_any_selectors_spec.rb +4 -0
  59. data/lib/capybara/spec/session/has_button_spec.rb +24 -0
  60. data/lib/capybara/spec/session/has_current_path_spec.rb +2 -2
  61. data/lib/capybara/spec/session/has_field_spec.rb +25 -1
  62. data/lib/capybara/spec/session/has_link_spec.rb +30 -0
  63. data/lib/capybara/spec/session/has_select_spec.rb +4 -4
  64. data/lib/capybara/spec/session/has_selector_spec.rb +15 -0
  65. data/lib/capybara/spec/session/has_text_spec.rb +2 -6
  66. data/lib/capybara/spec/session/node_spec.rb +43 -1
  67. data/lib/capybara/spec/session/scroll_spec.rb +4 -4
  68. data/lib/capybara/spec/session/visit_spec.rb +20 -0
  69. data/lib/capybara/spec/session/window/window_spec.rb +1 -1
  70. data/lib/capybara/spec/spec_helper.rb +4 -3
  71. data/lib/capybara/spec/test_app.rb +66 -8
  72. data/lib/capybara/spec/views/animated.erb +1 -1
  73. data/lib/capybara/spec/views/form.erb +11 -3
  74. data/lib/capybara/spec/views/frame_child.erb +1 -1
  75. data/lib/capybara/spec/views/frame_one.erb +1 -1
  76. data/lib/capybara/spec/views/frame_parent.erb +1 -1
  77. data/lib/capybara/spec/views/frame_two.erb +1 -1
  78. data/lib/capybara/spec/views/initial_alert.erb +2 -1
  79. data/lib/capybara/spec/views/layout.erb +10 -0
  80. data/lib/capybara/spec/views/obscured.erb +1 -1
  81. data/lib/capybara/spec/views/offset.erb +2 -1
  82. data/lib/capybara/spec/views/path.erb +2 -2
  83. data/lib/capybara/spec/views/popup_one.erb +1 -1
  84. data/lib/capybara/spec/views/popup_two.erb +1 -1
  85. data/lib/capybara/spec/views/react.erb +2 -2
  86. data/lib/capybara/spec/views/scroll.erb +2 -1
  87. data/lib/capybara/spec/views/spatial.erb +1 -1
  88. data/lib/capybara/spec/views/with_animation.erb +2 -3
  89. data/lib/capybara/spec/views/with_base_tag.erb +2 -2
  90. data/lib/capybara/spec/views/with_dragula.erb +2 -2
  91. data/lib/capybara/spec/views/with_fixed_header_footer.erb +2 -1
  92. data/lib/capybara/spec/views/with_hover.erb +2 -2
  93. data/lib/capybara/spec/views/with_html.erb +1 -1
  94. data/lib/capybara/spec/views/with_jquery_animation.erb +1 -1
  95. data/lib/capybara/spec/views/with_js.erb +2 -3
  96. data/lib/capybara/spec/views/with_jstree.erb +1 -1
  97. data/lib/capybara/spec/views/with_namespace.erb +1 -0
  98. data/lib/capybara/spec/views/with_shadow.erb +31 -0
  99. data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
  100. data/lib/capybara/spec/views/with_sortable_js.erb +2 -2
  101. data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
  102. data/lib/capybara/spec/views/with_windows.erb +1 -1
  103. data/lib/capybara/spec/views/within_frames.erb +1 -1
  104. data/lib/capybara/version.rb +1 -1
  105. data/lib/capybara/window.rb +1 -1
  106. data/lib/capybara.rb +19 -22
  107. data/spec/basic_node_spec.rb +16 -3
  108. data/spec/dsl_spec.rb +3 -3
  109. data/spec/fixtures/selenium_driver_rspec_failure.rb +2 -2
  110. data/spec/fixtures/selenium_driver_rspec_success.rb +2 -2
  111. data/spec/rack_test_spec.rb +20 -10
  112. data/spec/result_spec.rb +32 -35
  113. data/spec/rspec/features_spec.rb +3 -3
  114. data/spec/rspec/scenarios_spec.rb +1 -1
  115. data/spec/rspec/shared_spec_matchers.rb +2 -2
  116. data/spec/sauce_spec_chrome.rb +3 -3
  117. data/spec/selector_spec.rb +2 -2
  118. data/spec/selenium_spec_chrome.rb +9 -10
  119. data/spec/selenium_spec_chrome_remote.rb +9 -8
  120. data/spec/selenium_spec_firefox.rb +8 -3
  121. data/spec/selenium_spec_firefox_remote.rb +2 -2
  122. data/spec/selenium_spec_ie.rb +3 -6
  123. data/spec/selenium_spec_safari.rb +31 -19
  124. data/spec/server_spec.rb +5 -5
  125. data/spec/shared_selenium_node.rb +0 -4
  126. data/spec/shared_selenium_session.rb +20 -10
  127. data/spec/spec_helper.rb +1 -1
  128. metadata +37 -14
  129. data/lib/capybara/spec/views/with_title.erb +0 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 516861530f59d3d9713daba7379838146575b920f787ac2c3f5c4bbffc6bf44d
4
- data.tar.gz: 85b3fb81bf05e43122c2eef320a7a2dbd500033723ebbc04ebc414b0a42d0881
3
+ metadata.gz: c7283f19c4364fa2c19379e8da670105ebf59754937fc80117d975cd89959b87
4
+ data.tar.gz: b4fe7210ee210667602e9ec0a9882a1a34d8a64ceddae4df888aba0dd6974940
5
5
  SHA512:
6
- metadata.gz: 7f745dac92b73afa56f43a2a509052c7fc5d1efa458863b0b3f05507c664847d6f67720a2c2a680e21126888e63a6eaf6a0e9be7d1a4df9d6aaa9ec61d832e29
7
- data.tar.gz: 53532156dd31631879a0a8fda93126f259a5cd856a6cec82e6283f9f96e1cc2d98b788e4414ffee5756024d14819450d358529aa90d4c948618e82b878342ebd
6
+ metadata.gz: 71de08c12fdd479453fa743e0a7ef858e72ca96d9a32d2ecb0b1721b365a1f1e494309432b52536dcac5669af78fe50e60b1a9f886f0b08a2ac2fd378abfc52e
7
+ data.tar.gz: 91a2e75a15768ef02e8c9b9f1400c4ee1dd2e1e2562b4733dc520afddc4c71be962fcd5c0379481fa65938d0776cfe5434c75f5faf0e512246abca48b06b0c00
data/History.md CHANGED
@@ -1,7 +1,64 @@
1
+ # Version 3.37.1
2
+ Relesae date: 2022-05-09
3
+
4
+ ### Fixed
5
+
6
+ * Regression in rack-test visit - Issue #2548
7
+
8
+ # Version 3.37.0
9
+ Release date: 2022-05-07
10
+
11
+ ### Changed
12
+
13
+ * Ruby 2.7.0+ is now required
14
+
15
+ ### Added
16
+
17
+ * [Beta] CSP nonces inserted into animation disabler additions - Issue #2542
18
+ * Support `<base>` element in rack-test driver - ISsue #2544
19
+ * [Beta] `Element#shadow_root` support. Requires selenium-webdriver 4.1+. Only currently supported with Chrome when using the selenium driver. Note: only CSS can be used to find elements from the shadow root. Therefore you won't be able to use most Capybara helper methods (`fill_in`, `click_link`, `find_field`, etc) directly from the shadow root since those locators are built using XPath. If you first locate a descendant from the shadow root using CSS then you should be able to use all the Capybara methods from there.
20
+ * Regexp now supported for `exact_text` finder option
21
+
22
+ ### Fixed
23
+
24
+ * Fragments in referer headers in rack-test driver - Issue #2525
25
+ * Selenium v4.1 deprecation notice
26
+
27
+ # Version 3.36.0
28
+ Release date: 2021-10-24
29
+
30
+ ### Changed
31
+
32
+ * Ruby 2.6.0+ is now required
33
+ * Minimum selenium-webdriver supported is now 3.142.7
34
+
35
+ ### Added
36
+
37
+ * Support for selenium-webdriver 4.x
38
+ * `allow_label_click` accepts click options to be used when clicking an associated label
39
+ * Deprecated `allow_gumbo=` in favor of `use_html5_parsing=` to enable use of Nokogiri::HTML5 when available
40
+ * `Session#active_element` returns the element with focus - Not supported by the `RackTest` driver [Sean Doyle]
41
+ * Support `focused:` filter for finding interactive elements - Not supported by the `RackTest` driver [Sean Doyle]
42
+
43
+ ### Fixed
44
+
45
+ * Sibling and ancestor queries now work with Simple::Node - Issue #2452
46
+ * rack_test correctly ignores readonly attribute on specific input element types
47
+ * `Node#all_text` always returns a string - Issue #2477
48
+ * `have_any_of_selectors` negated match - Issue #2473
49
+ * `Document#scroll_to` fixed for standards behavior - pass quirks: true if you need the older behavior [Eric Anderson]
50
+ * Use capture on attach file event listener for better React compatibility [Jeff Way]
51
+ * Animation disabler produces valid HTML [Javi Martin]
52
+
53
+ ### Removed
54
+
55
+ * References to non-w3c mode in drivers/tests. Non-w3c mode is obsolete and no one should be using it anymore. Capybara hasn't been testing/supporting it in a while
56
+
1
57
  # Version 3.35.3
2
58
  Release date: 2021-01-29
3
59
 
4
60
  ### Fixed
61
+
5
62
  * Just a release to have the correct dates in the History.md in released gem
6
63
 
7
64
  # Version 3.35.2
@@ -1898,4 +1955,3 @@ too many users, please read the release notes carefully!
1898
1955
  * clicking links where the image's alt attribute contains the text is now possible
1899
1956
  * within_fieldset and within_table work when the default selector is CSS
1900
1957
  * boolean attributes work the same across drivers (return true/false)
1901
-
data/README.md CHANGED
@@ -74,7 +74,7 @@ GitHub): http://groups.google.com/group/ruby-capybara
74
74
 
75
75
  ## <a name="setup"></a>Setup
76
76
 
77
- Capybara requires Ruby 2.5.0 or later. To install, add this line to your
77
+ Capybara requires Ruby 2.7.0 or later. To install, add this line to your
78
78
  `Gemfile` and run `bundle install`:
79
79
 
80
80
  ```ruby
@@ -151,6 +151,8 @@ RSpec](https://relishapp.com/rspec/rspec-rails/v/4-0/docs/directory-structure))
151
151
  and if you have your Capybara specs in a different directory, then tag the
152
152
  example groups with `type: :feature` or `type: :system` depending on which type of test you're writing.
153
153
 
154
+ If you are using Rails system specs please see [their documentation](https://relishapp.com/rspec/rspec-rails/docs/system-specs/system-spec#system-specs-driven-by-selenium-chrome-headless) for selecting the driver you wish to use.
155
+
154
156
  If you are not using Rails, tag all the example groups in which you want to use
155
157
  Capybara with `type: :feature`.
156
158
 
@@ -258,6 +260,8 @@ end
258
260
 
259
261
  ## <a name="using-capybara-with-minitest"></a>Using Capybara with Minitest
260
262
 
263
+ * If you are using Rails system tests please see their documentation for information on selecting the driver you wish to use.
264
+
261
265
  * If you are using Rails, but not using Rails system tests, add the following code in your `test_helper.rb`
262
266
  file to make Capybara available in all test cases deriving from
263
267
  `ActionDispatch::IntegrationTest`:
@@ -7,10 +7,12 @@ module Capybara
7
7
  class Config
8
8
  extend Forwardable
9
9
 
10
- OPTIONS = %i[app reuse_server threadsafe server default_driver javascript_driver allow_gumbo].freeze
10
+ OPTIONS = %i[
11
+ app reuse_server threadsafe server default_driver javascript_driver use_html5_parsing allow_gumbo
12
+ ].freeze
11
13
 
12
- attr_accessor :app, :allow_gumbo
13
- attr_reader :reuse_server, :threadsafe, :session_options
14
+ attr_accessor :app, :use_html5_parsing
15
+ attr_reader :reuse_server, :threadsafe, :session_options # rubocop:disable Style/BisectedAttrAccessor
14
16
  attr_writer :default_driver, :javascript_driver
15
17
 
16
18
  SessionConfig::OPTIONS.each do |method|
@@ -22,7 +24,7 @@ module Capybara
22
24
  @javascript_driver = nil
23
25
  end
24
26
 
25
- attr_writer :reuse_server
27
+ attr_writer :reuse_server # rubocop:disable Style/BisectedAttrAccessor
26
28
 
27
29
  def threadsafe=(bool)
28
30
  if (bool != threadsafe) && Session.instance_created?
@@ -88,5 +90,15 @@ module Capybara
88
90
  end
89
91
  @deprecation_notified[method] = true
90
92
  end
93
+
94
+ def allow_gumbo=(val)
95
+ deprecate('allow_gumbo=', 'use_html5_parsing=')
96
+ self.use_html5_parsing = val
97
+ end
98
+
99
+ def allow_gumbo
100
+ deprecate('allow_gumbo', 'use_html5_parsing')
101
+ use_html5_parsing
102
+ end
91
103
  end
92
104
  end
@@ -63,6 +63,10 @@ class Capybara::Driver::Base
63
63
  raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#send_keys'
64
64
  end
65
65
 
66
+ def active_element
67
+ raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#active_element'
68
+ end
69
+
66
70
  ##
67
71
  #
68
72
  # @param frame [Capybara::Node::Element, :parent, :top] The iframe element to switch to
@@ -125,6 +125,10 @@ module Capybara
125
125
  raise NotSupportedByDriverError, 'Capybara::Driver::Node#trigger'
126
126
  end
127
127
 
128
+ def shadow_root
129
+ raise NotSupportedByDriverError, 'Capybara::Driver::Node#shadow_root'
130
+ end
131
+
128
132
  def inspect
129
133
  %(#<#{self.class} tag="#{tag_name}" path="#{path}">)
130
134
  rescue NotSupportedByDriverError
@@ -132,7 +136,7 @@ module Capybara
132
136
  end
133
137
 
134
138
  def ==(other)
135
- raise NotSupportedByDriverError, 'Capybara::Driver::Node#=='
139
+ eql?(other) || (other.respond_to?(:native) && native == other.native)
136
140
  end
137
141
  end
138
142
  end
data/lib/capybara/dsl.rb CHANGED
@@ -47,17 +47,11 @@ module Capybara
47
47
  end
48
48
 
49
49
  Session::DSL_METHODS.each do |method|
50
- if RUBY_VERSION >= '2.7'
51
- class_eval <<~METHOD, __FILE__, __LINE__ + 1
52
- def #{method}(...)
53
- page.method("#{method}").call(...)
54
- end
55
- METHOD
56
- else
57
- define_method method do |*args, &block|
58
- page.send method, *args, &block
50
+ class_eval <<~METHOD, __FILE__, __LINE__ + 1
51
+ def #{method}(...)
52
+ page.method("#{method}").call(...)
59
53
  end
60
- end
54
+ METHOD
61
55
  end
62
56
  end
63
57
 
@@ -73,25 +73,16 @@ module Capybara
73
73
  def filter_backtrace(trace)
74
74
  return 'No backtrace' unless trace
75
75
 
76
- filter = %r{lib/capybara/|lib/rspec/|lib/minitest/}
76
+ filter = %r{lib/capybara/|lib/rspec/|lib/minitest/|delegate.rb}
77
77
  new_trace = trace.take_while { |line| line !~ filter }
78
- new_trace = trace.reject { |line| line =~ filter } if new_trace.empty?
78
+ new_trace = trace.grep_v(filter) if new_trace.empty?
79
79
  new_trace = trace.dup if new_trace.empty?
80
80
 
81
81
  new_trace.first.split(/:in /, 2).first
82
82
  end
83
83
 
84
84
  def warn(message, uplevel: 1)
85
- return Kernel.warn(message, uplevel: uplevel) if RUBY_VERSION >= '2.6'
86
-
87
- # TODO: Remove when we drop support for Ruby 2.5
88
- # Workaround for emulating `warn '...', uplevel: n` in Ruby 2.5 or lower.
89
- if (match = /^(?<file>.+?):(?<line>\d+)(?::in `.*')?/.match(caller[uplevel]))
90
- location = [match[:file], match[:line]].join(':')
91
- Kernel.warn "#{location}: #{message}"
92
- else
93
- Kernel.warn message
94
- end
85
+ Kernel.warn(message, uplevel: uplevel)
95
86
  end
96
87
 
97
88
  if defined?(Process::CLOCK_MONOTONIC)
@@ -246,9 +246,9 @@ module Capybara
246
246
 
247
247
  ##
248
248
  # @deprecated
249
- def must_have_style(*args, **kw_args, &block)
249
+ def must_have_style(...)
250
250
  warn 'must_have_style is deprecated, please use must_match_style'
251
- must_match_style(*args, **kw_args, &block)
251
+ must_match_style(...)
252
252
  end
253
253
  end
254
254
  end
@@ -92,8 +92,9 @@ module Capybara
92
92
  end
93
93
 
94
94
  # @!macro label_click
95
- # @option options [Boolean] allow_label_click
95
+ # @option options [Boolean, Hash] allow_label_click
96
96
  # Attempt to click the label to toggle state if element is non-visible. Defaults to {Capybara.configure automatic_label_click}.
97
+ # If set to a Hash it is passed as options to the `click` on the label
97
98
 
98
99
  ##
99
100
  #
@@ -277,7 +278,7 @@ module Capybara
277
278
  # @return [Capybara::Node::Element] The file field element
278
279
  def attach_file(locator = nil, paths, make_visible: nil, **options) # rubocop:disable Style/OptionalArguments
279
280
  if locator && block_given?
280
- raise ArgumentError, '``#attach_file` does not support passing both a locator and a block'
281
+ raise ArgumentError, '`#attach_file` does not support passing both a locator and a block'
281
282
  end
282
283
 
283
284
  Array(paths).each do |path|
@@ -314,7 +315,7 @@ module Capybara
314
315
 
315
316
  begin
316
317
  find(:datalist_input, from, **options)
317
- rescue Capybara::ElementNotFound => dlinput_error # rubocop:disable Naming/RescuedExceptionsVariableName
318
+ rescue Capybara::ElementNotFound => dlinput_error
318
319
  raise Capybara::ElementNotFound, "#{select_error.message} and #{dlinput_error.message}"
319
320
  end
320
321
  end
@@ -371,7 +372,11 @@ module Capybara
371
372
 
372
373
  begin
373
374
  el ||= find(selector, locator, **options.merge(visible: :all))
374
- el.session.find(:label, for: el, visible: true, match: :first).click unless el.checked? == checked
375
+ unless el.checked? == checked
376
+ el.session
377
+ .find(:label, for: el, visible: true, match: :first)
378
+ .click(**(Hash.try_convert(allow_label_click) || {}))
379
+ end
375
380
  rescue StandardError # swallow extra errors - raise original
376
381
  raise e
377
382
  end
@@ -408,7 +413,7 @@ module Capybara
408
413
  this.removeEventListener('click', file_catcher);
409
414
  e.preventDefault();
410
415
  }
411
- })
416
+ }, {capture: true})
412
417
  JS
413
418
  end
414
419
  end
@@ -40,8 +40,8 @@ module Capybara
40
40
  find(:xpath, '/html').evaluate_script(*args)
41
41
  end
42
42
 
43
- def scroll_to(*args, **options)
44
- find(:xpath, '//body').scroll_to(*args, **options)
43
+ def scroll_to(*args, quirks: false, **options)
44
+ find(:xpath, quirks ? '//body' : '/html').scroll_to(*args, **options)
45
45
  end
46
46
  end
47
47
  end
@@ -115,7 +115,7 @@ module Capybara
115
115
  #
116
116
  # @return [Capybara::Node::Element] The element
117
117
  def set(value, **options)
118
- if ENV['CAPYBARA_THOROUGH'] && readonly?
118
+ if ENV.fetch('CAPYBARA_THOROUGH', nil) && readonly?
119
119
  raise Capybara::ReadOnlyElementError, "Attempt to set readonly element with value: #{value}"
120
120
  end
121
121
 
@@ -472,6 +472,17 @@ module Capybara
472
472
  self
473
473
  end
474
474
 
475
+ ##
476
+ #
477
+ # Return the shadow_root for the current element
478
+ #
479
+ # @return [Capybara::Node::Element] The shadow root
480
+
481
+ def shadow_root
482
+ root = synchronize { base.shadow_root }
483
+ root && Capybara::Node::Element.new(session, root, nil, nil)
484
+ end
485
+
475
486
  ##
476
487
  #
477
488
  # Execute the given JS in the context of the element not returning a result. This is useful for scripts that return
@@ -550,7 +561,7 @@ module Capybara
550
561
  return self unless @allow_reload
551
562
 
552
563
  begin
553
- reloaded = @query.resolve_for(query_scope.reload)[@query_idx.to_i]
564
+ reloaded = @query.resolve_for(query_scope ? query_scope.reload : session)[@query_idx.to_i]
554
565
  @base = reloaded.base if reloaded
555
566
  rescue StandardError => e
556
567
  raise e unless catch_error?(e)
@@ -18,11 +18,11 @@ module Capybara
18
18
  #
19
19
  # @!macro system_filters
20
20
  # @option options [String, Regexp] text Only find elements which contain this text or match this regexp
21
- # @option options [String, Boolean] exact_text
21
+ # @option options [String, Regexp, String] exact_text
22
22
  # When String the elements contained text must match exactly, when Boolean controls whether the `text` option must match exactly.
23
23
  # Defaults to {Capybara.configure exact_text}.
24
24
  # @option options [Boolean] normalize_ws
25
- # Whether the `text`/`exact_text` options are compared against elment text with whitespace normalized or as returned by the driver.
25
+ # Whether the `text`/`exact_text` options are compared against element text with whitespace normalized or as returned by the driver.
26
26
  # Defaults to {Capybara.configure default_normalize_ws}.
27
27
  # @option options [Boolean, Symbol] visible
28
28
  # Only find elements with the specified visibility. Defaults to behavior indicated by {Capybara.configure ignore_hidden_elements}.
@@ -50,6 +50,13 @@ module Capybara
50
50
  #
51
51
  def find(*args, **options, &optional_filter_block)
52
52
  options[:session_options] = session_options
53
+ count_options = options.slice(*Capybara::Queries::BaseQuery::COUNT_KEYS)
54
+ unless count_options.empty?
55
+ Capybara::Helpers.warn(
56
+ "'find' does not support count options (#{count_options}) ignoring. " \
57
+ "Called from: #{Capybara::Helpers.filter_backtrace(caller)}"
58
+ )
59
+ end
53
60
  synced_resolve Capybara::Queries::SelectorQuery.new(*args, **options, &optional_filter_block)
54
61
  end
55
62
 
@@ -110,7 +110,7 @@ module Capybara
110
110
  # No need for an xpath if only checking the current element
111
111
  !(native.key?('hidden') ||
112
112
  /display:\s?none/.match?(native[:style] || '') ||
113
- %w[script head].include?(tag_name))
113
+ %w[script head style].include?(tag_name))
114
114
  end
115
115
  end
116
116
 
@@ -191,6 +191,10 @@ module Capybara
191
191
  {}
192
192
  end
193
193
 
194
+ def ==(other)
195
+ eql?(other) || (other.respond_to?(:native) && native == other.native)
196
+ end
197
+
194
198
  private
195
199
 
196
200
  def option_value(option)
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capybara
4
+ # @api private
5
+ module Queries
6
+ class ActiveElementQuery < BaseQuery
7
+ def initialize(**options)
8
+ @options = options
9
+ super(@options)
10
+ end
11
+
12
+ def resolve_for(session)
13
+ node = session.driver.active_element
14
+ [Capybara::Node::Element.new(session, node, nil, self)]
15
+ end
16
+ end
17
+ end
18
+ end
@@ -8,7 +8,8 @@ module Capybara
8
8
  @child_node = node
9
9
 
10
10
  node.synchronize do
11
- match_results = super(node.session.current_scope, exact)
11
+ scope = node.respond_to?(:session) ? node.session.current_scope : node.find(:xpath, '/*')
12
+ match_results = super(scope, exact)
12
13
  ancestors = node.find_xpath(XPath.ancestor.to_s)
13
14
  .map(&method(:to_element))
14
15
  .select { |el| match_results.include?(el) }
@@ -19,7 +19,7 @@ module Capybara
19
19
 
20
20
  def resolves_for?(session)
21
21
  uri = ::Addressable::URI.parse(session.current_url)
22
- @actual_path = (options[:ignore_query] ? uri&.omit(:query) : uri).yield_self do |u|
22
+ @actual_path = (options[:ignore_query] ? uri&.omit(:query) : uri).then do |u|
23
23
  options[:url] ? u&.to_s : u&.request_uri
24
24
  end
25
25
 
@@ -9,7 +9,7 @@ module Capybara
9
9
 
10
10
  SPATIAL_KEYS = %i[above below left_of right_of near].freeze
11
11
  VALID_KEYS = SPATIAL_KEYS + COUNT_KEYS +
12
- %i[text id class style visible obscured exact exact_text normalize_ws match wait filter_set]
12
+ %i[text id class style visible obscured exact exact_text normalize_ws match wait filter_set focused]
13
13
  VALID_MATCH = %i[first smart prefer_exact one].freeze
14
14
 
15
15
  def initialize(*args,
@@ -27,6 +27,12 @@ module Capybara
27
27
  @order = order
28
28
  @filter_cache = Hash.new { |hsh, key| hsh[key] = {} }
29
29
 
30
+ if @options[:text].is_a?(Regexp) && [true, false].include?(@options[:exact_text])
31
+ Capybara::Helpers.warn(
32
+ "Boolean 'exact_text' option is not supported when 'text' option is a Regexp - ignoring"
33
+ )
34
+ end
35
+
30
36
  super(@options)
31
37
  self.session_options = session_options
32
38
 
@@ -73,6 +79,8 @@ module Capybara
73
79
 
74
80
  desc << " with id #{options[:id]}" if options[:id]
75
81
  desc << " with classes [#{Array(options[:class]).join(',')}]" if options[:class]
82
+ desc << ' that is focused' if options[:focused]
83
+ desc << ' that is not focused' if options[:focused] == false
76
84
 
77
85
  desc << case options[:style]
78
86
  when String
@@ -371,6 +379,10 @@ module Capybara
371
379
  options.key?(:style) && !custom_keys.include?(:style)
372
380
  end
373
381
 
382
+ def use_default_focused_filter?
383
+ options.key?(:focused) && !custom_keys.include?(:focused)
384
+ end
385
+
374
386
  def use_spatial_filter?
375
387
  options.values_at(*SPATIAL_KEYS).compact.any?
376
388
  end
@@ -435,6 +447,7 @@ module Capybara
435
447
  matches_id_filter?(node) &&
436
448
  matches_class_filter?(node) &&
437
449
  matches_style_filter?(node) &&
450
+ matches_focused_filter?(node) &&
438
451
  matches_text_filter?(node) &&
439
452
  matches_exact_text_filter?(node)
440
453
  end
@@ -494,6 +507,12 @@ module Capybara
494
507
  end
495
508
  end
496
509
 
510
+ def matches_focused_filter?(node)
511
+ return true unless use_default_focused_filter?
512
+
513
+ (node == node.session.active_element) == options[:focused]
514
+ end
515
+
497
516
  def need_to_process_classes?
498
517
  case options[:class]
499
518
  when Regexp then true
@@ -528,16 +547,19 @@ module Capybara
528
547
  def matches_text_filter?(node)
529
548
  value = options[:text]
530
549
  return true unless value
531
- return matches_text_exactly?(node, value) if exact_text == true
550
+ return matches_text_exactly?(node, value) if exact_text == true && !value.is_a?(Regexp)
532
551
 
533
552
  regexp = value.is_a?(Regexp) ? value : Regexp.escape(value.to_s)
534
553
  matches_text_regexp?(node, regexp)
535
554
  end
536
555
 
537
556
  def matches_exact_text_filter?(node)
538
- return true unless exact_text.is_a?(String)
539
-
540
- matches_text_exactly?(node, exact_text)
557
+ case exact_text
558
+ when String, Regexp
559
+ matches_text_exactly?(node, exact_text)
560
+ else
561
+ true
562
+ end
541
563
  end
542
564
 
543
565
  def matches_visibility_filters?(node)
@@ -565,17 +587,21 @@ module Capybara
565
587
 
566
588
  def matches_text_exactly?(node, value)
567
589
  regexp = value.is_a?(Regexp) ? value : /\A#{Regexp.escape(value.to_s)}\z/
568
- matches_text_regexp?(node, regexp)
590
+ matches_text_regexp(node, regexp).then { |m| m&.pre_match == '' && m&.post_match == '' }
569
591
  end
570
592
 
571
593
  def normalize_ws
572
594
  options.fetch(:normalize_ws, session_options.default_normalize_ws)
573
595
  end
574
596
 
575
- def matches_text_regexp?(node, regexp)
597
+ def matches_text_regexp(node, regexp)
576
598
  text_visible = visible
577
599
  text_visible = :all if text_visible == :hidden
578
- node.text(text_visible, normalize_ws: normalize_ws).match?(regexp)
600
+ node.text(text_visible, normalize_ws: normalize_ws).match(regexp)
601
+ end
602
+
603
+ def matches_text_regexp?(node, regexp)
604
+ !matches_text_regexp(node, regexp).nil?
579
605
  end
580
606
 
581
607
  def default_visibility
@@ -7,7 +7,8 @@ module Capybara
7
7
  def resolve_for(node, exact = nil)
8
8
  @sibling_node = node
9
9
  node.synchronize do
10
- match_results = super(node.session.current_scope, exact)
10
+ scope = node.respond_to?(:session) ? node.session.current_scope : node.find(:xpath, '/*')
11
+ match_results = super(scope, exact)
11
12
  siblings = node.find_xpath((XPath.preceding_sibling + XPath.following_sibling).to_s)
12
13
  .map(&method(:to_element))
13
14
  .select { |el| match_results.include?(el) }