capybara 3.35.3 → 3.36.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.
- checksums.yaml +4 -4
- data/History.md +31 -1
- data/README.md +5 -1
- data/lib/capybara/config.rb +16 -4
- data/lib/capybara/driver/base.rb +4 -0
- data/lib/capybara/driver/node.rb +1 -1
- data/lib/capybara/helpers.rb +2 -11
- data/lib/capybara/node/actions.rb +10 -5
- data/lib/capybara/node/document.rb +2 -2
- data/lib/capybara/node/element.rb +1 -1
- data/lib/capybara/node/finders.rb +1 -1
- data/lib/capybara/node/simple.rb +5 -1
- data/lib/capybara/queries/active_element_query.rb +18 -0
- data/lib/capybara/queries/ancestor_query.rb +2 -1
- data/lib/capybara/queries/current_path_query.rb +1 -1
- data/lib/capybara/queries/selector_query.rb +14 -1
- data/lib/capybara/queries/sibling_query.rb +2 -1
- data/lib/capybara/rack_test/node.rb +9 -6
- data/lib/capybara/registrations/drivers.rb +3 -3
- data/lib/capybara/rspec/matcher_proxies.rb +3 -3
- data/lib/capybara/rspec/matchers/have_selector.rb +1 -1
- data/lib/capybara/selector/builders/css_builder.rb +1 -1
- data/lib/capybara/selector/builders/xpath_builder.rb +1 -1
- data/lib/capybara/selector/css.rb +1 -1
- data/lib/capybara/selector/definition/button.rb +9 -4
- data/lib/capybara/selector/definition/checkbox.rb +1 -1
- data/lib/capybara/selector/definition/file_field.rb +1 -1
- data/lib/capybara/selector/definition/fillable_field.rb +1 -1
- data/lib/capybara/selector/definition/radio_button.rb +1 -1
- data/lib/capybara/selector/definition.rb +2 -1
- data/lib/capybara/selector/filter_set.rb +4 -6
- data/lib/capybara/selector.rb +1 -0
- data/lib/capybara/selenium/driver.rb +19 -9
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +1 -1
- data/lib/capybara/selenium/driver_specializations/edge_driver.rb +1 -1
- data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +1 -1
- data/lib/capybara/selenium/node.rb +10 -8
- data/lib/capybara/selenium/nodes/chrome_node.rb +1 -1
- data/lib/capybara/selenium/nodes/edge_node.rb +1 -1
- data/lib/capybara/selenium/nodes/firefox_node.rb +1 -1
- data/lib/capybara/selenium/nodes/safari_node.rb +2 -2
- data/lib/capybara/server/animation_disabler.rb +15 -9
- data/lib/capybara/session.rb +12 -2
- data/lib/capybara/spec/session/active_element_spec.rb +31 -0
- data/lib/capybara/spec/session/all_spec.rb +4 -6
- data/lib/capybara/spec/session/check_spec.rb +9 -0
- data/lib/capybara/spec/session/choose_spec.rb +6 -0
- data/lib/capybara/spec/session/has_any_selectors_spec.rb +4 -0
- data/lib/capybara/spec/session/has_button_spec.rb +24 -0
- data/lib/capybara/spec/session/has_field_spec.rb +24 -0
- data/lib/capybara/spec/session/has_link_spec.rb +24 -0
- data/lib/capybara/spec/session/has_text_spec.rb +1 -1
- data/lib/capybara/spec/session/node_spec.rb +1 -1
- data/lib/capybara/spec/session/scroll_spec.rb +4 -4
- data/lib/capybara/spec/spec_helper.rb +4 -3
- data/lib/capybara/spec/test_app.rb +17 -8
- data/lib/capybara/spec/views/animated.erb +1 -1
- data/lib/capybara/spec/views/form.erb +11 -3
- data/lib/capybara/spec/views/frame_child.erb +1 -1
- data/lib/capybara/spec/views/frame_one.erb +1 -1
- data/lib/capybara/spec/views/frame_parent.erb +1 -1
- data/lib/capybara/spec/views/frame_two.erb +1 -1
- data/lib/capybara/spec/views/initial_alert.erb +2 -1
- data/lib/capybara/spec/views/layout.erb +10 -0
- data/lib/capybara/spec/views/obscured.erb +1 -1
- data/lib/capybara/spec/views/offset.erb +2 -1
- data/lib/capybara/spec/views/path.erb +2 -2
- data/lib/capybara/spec/views/popup_one.erb +1 -1
- data/lib/capybara/spec/views/popup_two.erb +1 -1
- data/lib/capybara/spec/views/react.erb +2 -2
- data/lib/capybara/spec/views/scroll.erb +2 -1
- data/lib/capybara/spec/views/spatial.erb +1 -1
- data/lib/capybara/spec/views/with_animation.erb +2 -3
- data/lib/capybara/spec/views/with_base_tag.erb +2 -2
- data/lib/capybara/spec/views/with_dragula.erb +2 -2
- data/lib/capybara/spec/views/with_fixed_header_footer.erb +2 -1
- data/lib/capybara/spec/views/with_hover.erb +2 -2
- data/lib/capybara/spec/views/with_html.erb +1 -1
- data/lib/capybara/spec/views/with_jquery_animation.erb +1 -1
- data/lib/capybara/spec/views/with_js.erb +2 -3
- data/lib/capybara/spec/views/with_jstree.erb +1 -1
- data/lib/capybara/spec/views/with_namespace.erb +1 -0
- data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
- data/lib/capybara/spec/views/with_sortable_js.erb +2 -2
- data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
- data/lib/capybara/spec/views/with_windows.erb +1 -1
- data/lib/capybara/spec/views/within_frames.erb +1 -1
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara.rb +18 -22
- data/spec/basic_node_spec.rb +16 -3
- data/spec/dsl_spec.rb +1 -1
- data/spec/fixtures/selenium_driver_rspec_success.rb +1 -1
- data/spec/rack_test_spec.rb +14 -10
- data/spec/result_spec.rb +5 -6
- data/spec/rspec/features_spec.rb +1 -1
- data/spec/rspec/shared_spec_matchers.rb +2 -2
- data/spec/selenium_spec_chrome.rb +8 -9
- data/spec/selenium_spec_chrome_remote.rb +9 -8
- data/spec/selenium_spec_firefox.rb +4 -3
- data/spec/selenium_spec_firefox_remote.rb +2 -2
- data/spec/selenium_spec_ie.rb +3 -6
- data/spec/selenium_spec_safari.rb +27 -19
- data/spec/shared_selenium_node.rb +0 -4
- data/spec/shared_selenium_session.rb +11 -8
- metadata +35 -14
- 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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fd7f81974b21d47d35c99dff02f878607128c99c80b18b1fef54680797978898
|
|
4
|
+
data.tar.gz: ee6659925b7ddaaa0220ad0af81ae65a34e96094583308c160902470e7019128
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ad7d2ea4d2612770e801443e61b23a45c0b9936fa4387170ffbbf051706dbc8aee5111b8eefb2d6fabb3042b479dc19d26248923d84da3e78d12dc090aca1e2d
|
|
7
|
+
data.tar.gz: 8868469dc6ddcb6c802f4a7b76df9455ff561d00ad78e3cab75e5f02e1b71b74402700c081864368d446304acb6fac2fed5cb79df117d309d4a0a278c95e35d8
|
data/History.md
CHANGED
|
@@ -1,7 +1,38 @@
|
|
|
1
|
+
# Version 3.36.0
|
|
2
|
+
Release date: 2021-10-24
|
|
3
|
+
|
|
4
|
+
### Changed
|
|
5
|
+
|
|
6
|
+
* Ruby 2.6.0+ is now required
|
|
7
|
+
* Minimum selenium-webdriver supported is now 3.142.7
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
* Support for selenium-webdriver 4.x
|
|
12
|
+
* `allow_label_click` accepts click options to be used when clicking an associated label
|
|
13
|
+
* Deprecated `allow_gumbo=` in favor of `use_html5_parsing=` to enable use of Nokogiri::HTL5 when available
|
|
14
|
+
* `Session#active_element` returns the element with focus - Not supported by the `RackTest` driver [Sean Doyle]
|
|
15
|
+
* Support `focused:` filter for finding interactive elements - Not supported by the `RackTest` driver [Sean Doyle]
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
* Sibling and ancestor queries now work with Simple::Node - Issue #2452
|
|
20
|
+
* rack_test correctly ignores readonly attribute on specific input element types
|
|
21
|
+
* `Node#all_text` always returns a string - Issue #2477
|
|
22
|
+
* `have_any_of_selectors` negated match - Issue #2473
|
|
23
|
+
* `Document#scroll_to` fixed for standards behavior - pass quirks: true if you need the older behavior [Eric Anderson]
|
|
24
|
+
* Use capture on attach file event listener for better React compatibility [Jeff Way]
|
|
25
|
+
* Animation disabler produces valid HTML [Javi Martin]
|
|
26
|
+
|
|
27
|
+
### Removed
|
|
28
|
+
|
|
29
|
+
* 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
|
|
30
|
+
|
|
1
31
|
# Version 3.35.3
|
|
2
32
|
Release date: 2021-01-29
|
|
3
33
|
|
|
4
34
|
### Fixed
|
|
35
|
+
|
|
5
36
|
* Just a release to have the correct dates in the History.md in released gem
|
|
6
37
|
|
|
7
38
|
# Version 3.35.2
|
|
@@ -1898,4 +1929,3 @@ too many users, please read the release notes carefully!
|
|
|
1898
1929
|
* clicking links where the image's alt attribute contains the text is now possible
|
|
1899
1930
|
* within_fieldset and within_table work when the default selector is CSS
|
|
1900
1931
|
* 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.
|
|
77
|
+
Capybara requires Ruby 2.6.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`:
|
data/lib/capybara/config.rb
CHANGED
|
@@ -7,10 +7,12 @@ module Capybara
|
|
|
7
7
|
class Config
|
|
8
8
|
extend Forwardable
|
|
9
9
|
|
|
10
|
-
OPTIONS = %i[
|
|
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, :
|
|
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
|
data/lib/capybara/driver/base.rb
CHANGED
|
@@ -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
|
data/lib/capybara/driver/node.rb
CHANGED
data/lib/capybara/helpers.rb
CHANGED
|
@@ -75,23 +75,14 @@ module Capybara
|
|
|
75
75
|
|
|
76
76
|
filter = %r{lib/capybara/|lib/rspec/|lib/minitest/}
|
|
77
77
|
new_trace = trace.take_while { |line| line !~ filter }
|
|
78
|
-
new_trace = trace.
|
|
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
|
-
|
|
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)
|
|
@@ -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, '
|
|
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
|
|
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
|
-
|
|
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
|
|
@@ -550,7 +550,7 @@ module Capybara
|
|
|
550
550
|
return self unless @allow_reload
|
|
551
551
|
|
|
552
552
|
begin
|
|
553
|
-
reloaded = @query.resolve_for(query_scope.reload)[@query_idx.to_i]
|
|
553
|
+
reloaded = @query.resolve_for(query_scope ? query_scope.reload : session)[@query_idx.to_i]
|
|
554
554
|
@base = reloaded.base if reloaded
|
|
555
555
|
rescue StandardError => e
|
|
556
556
|
raise e unless catch_error?(e)
|
|
@@ -22,7 +22,7 @@ module Capybara
|
|
|
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
|
|
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}.
|
data/lib/capybara/node/simple.rb
CHANGED
|
@@ -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
|
-
|
|
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).
|
|
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,
|
|
@@ -73,6 +73,8 @@ module Capybara
|
|
|
73
73
|
|
|
74
74
|
desc << " with id #{options[:id]}" if options[:id]
|
|
75
75
|
desc << " with classes [#{Array(options[:class]).join(',')}]" if options[:class]
|
|
76
|
+
desc << ' that is focused' if options[:focused]
|
|
77
|
+
desc << ' that is not focused' if options[:focused] == false
|
|
76
78
|
|
|
77
79
|
desc << case options[:style]
|
|
78
80
|
when String
|
|
@@ -371,6 +373,10 @@ module Capybara
|
|
|
371
373
|
options.key?(:style) && !custom_keys.include?(:style)
|
|
372
374
|
end
|
|
373
375
|
|
|
376
|
+
def use_default_focused_filter?
|
|
377
|
+
options.key?(:focused) && !custom_keys.include?(:focused)
|
|
378
|
+
end
|
|
379
|
+
|
|
374
380
|
def use_spatial_filter?
|
|
375
381
|
options.values_at(*SPATIAL_KEYS).compact.any?
|
|
376
382
|
end
|
|
@@ -435,6 +441,7 @@ module Capybara
|
|
|
435
441
|
matches_id_filter?(node) &&
|
|
436
442
|
matches_class_filter?(node) &&
|
|
437
443
|
matches_style_filter?(node) &&
|
|
444
|
+
matches_focused_filter?(node) &&
|
|
438
445
|
matches_text_filter?(node) &&
|
|
439
446
|
matches_exact_text_filter?(node)
|
|
440
447
|
end
|
|
@@ -494,6 +501,12 @@ module Capybara
|
|
|
494
501
|
end
|
|
495
502
|
end
|
|
496
503
|
|
|
504
|
+
def matches_focused_filter?(node)
|
|
505
|
+
return true unless use_default_focused_filter?
|
|
506
|
+
|
|
507
|
+
(node == node.session.active_element) == options[:focused]
|
|
508
|
+
end
|
|
509
|
+
|
|
497
510
|
def need_to_process_classes?
|
|
498
511
|
case options[:class]
|
|
499
512
|
when Regexp then true
|
|
@@ -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
|
-
|
|
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) }
|
|
@@ -108,6 +108,13 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
|
108
108
|
end
|
|
109
109
|
end
|
|
110
110
|
|
|
111
|
+
def readonly?
|
|
112
|
+
# readonly attribute not valid on these input types
|
|
113
|
+
return false if input_field? && %w[hidden range color checkbox radio file submit image reset button].include?(type)
|
|
114
|
+
|
|
115
|
+
super
|
|
116
|
+
end
|
|
117
|
+
|
|
111
118
|
def path
|
|
112
119
|
native.path
|
|
113
120
|
end
|
|
@@ -139,10 +146,6 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
|
139
146
|
end
|
|
140
147
|
end
|
|
141
148
|
|
|
142
|
-
def ==(other)
|
|
143
|
-
native == other.native
|
|
144
|
-
end
|
|
145
|
-
|
|
146
149
|
protected
|
|
147
150
|
|
|
148
151
|
# @api private
|
|
@@ -159,7 +162,7 @@ protected
|
|
|
159
162
|
end.join || ''
|
|
160
163
|
text = "\n#{text}\n" if BLOCK_ELEMENTS.include?(tag_name)
|
|
161
164
|
text
|
|
162
|
-
else
|
|
165
|
+
else # rubocop:disable Lint/DuplicateBranch
|
|
163
166
|
''
|
|
164
167
|
end
|
|
165
168
|
end
|
|
@@ -213,7 +216,7 @@ private
|
|
|
213
216
|
min, max, step = (native['min'] || 0).to_f, (native['max'] || 100).to_f, (native['step'] || 1).to_f
|
|
214
217
|
value = value.to_f
|
|
215
218
|
value = value.clamp(min, max)
|
|
216
|
-
value = ((value - min) / step).round * step + min
|
|
219
|
+
value = (((value - min) / step).round * step) + min
|
|
217
220
|
native['value'] = value.clamp(min, max)
|
|
218
221
|
end
|
|
219
222
|
|
|
@@ -14,7 +14,7 @@ Capybara.register_driver :selenium_headless do |app|
|
|
|
14
14
|
browser_options = ::Selenium::WebDriver::Firefox::Options.new.tap do |opts|
|
|
15
15
|
opts.add_argument '-headless'
|
|
16
16
|
end
|
|
17
|
-
Capybara::Selenium::Driver.new(app, **
|
|
17
|
+
Capybara::Selenium::Driver.new(app, **{ :browser => :firefox, options_key => browser_options })
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
Capybara.register_driver :selenium_chrome do |app|
|
|
@@ -25,7 +25,7 @@ Capybara.register_driver :selenium_chrome do |app|
|
|
|
25
25
|
opts.add_argument('--disable-site-isolation-trials')
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
Capybara::Selenium::Driver.new(app, **
|
|
28
|
+
Capybara::Selenium::Driver.new(app, **{ :browser => :chrome, options_key => browser_options })
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
Capybara.register_driver :selenium_chrome_headless do |app|
|
|
@@ -38,5 +38,5 @@ Capybara.register_driver :selenium_chrome_headless do |app|
|
|
|
38
38
|
opts.add_argument('--disable-site-isolation-trials')
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
Capybara::Selenium::Driver.new(app, **
|
|
41
|
+
Capybara::Selenium::Driver.new(app, **{ :browser => :chrome, options_key => browser_options })
|
|
42
42
|
end
|
|
@@ -23,7 +23,7 @@ end
|
|
|
23
23
|
if RUBY_ENGINE == 'jruby'
|
|
24
24
|
# :nocov:
|
|
25
25
|
module Capybara::DSL
|
|
26
|
-
class <<self
|
|
26
|
+
class << self
|
|
27
27
|
remove_method :included
|
|
28
28
|
|
|
29
29
|
def included(base)
|
|
@@ -55,7 +55,7 @@ else
|
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
def self.prepended(base)
|
|
58
|
-
class <<base
|
|
58
|
+
class << base
|
|
59
59
|
prepend ClassMethods
|
|
60
60
|
end
|
|
61
61
|
end
|
|
@@ -70,7 +70,7 @@ else
|
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
def self.prepended(base)
|
|
73
|
-
class <<base
|
|
73
|
+
class << base
|
|
74
74
|
prepend ClassMethods
|
|
75
75
|
end
|
|
76
76
|
end
|
|
@@ -76,7 +76,7 @@ module Capybara
|
|
|
76
76
|
else
|
|
77
77
|
cls = Array(classes).reject { |c| c.is_a? Regexp }.group_by { |cl| cl.match?(/^!(?!!!)/) }
|
|
78
78
|
[(cls[false].to_a.map { |cl| ".#{Capybara::Selector::CSS.escape(cl.sub(/^!!/, ''))}" } +
|
|
79
|
-
cls[true].to_a.map { |cl| ":not(.#{Capybara::Selector::CSS.escape(cl.slice(1
|
|
79
|
+
cls[true].to_a.map { |cl| ":not(.#{Capybara::Selector::CSS.escape(cl.slice(1..))})" }).join]
|
|
80
80
|
end
|
|
81
81
|
end
|
|
82
82
|
end
|
|
@@ -51,7 +51,7 @@ module Capybara
|
|
|
51
51
|
else
|
|
52
52
|
Array(classes).reject { |c| c.is_a? Regexp }.map do |klass|
|
|
53
53
|
if klass.match?(/^!(?!!!)/)
|
|
54
|
-
!XPath.attr(:class).contains_word(klass.slice(1
|
|
54
|
+
!XPath.attr(:class).contains_word(klass.slice(1..))
|
|
55
55
|
else
|
|
56
56
|
XPath.attr(:class).contains_word(klass.sub(/^!!/, ''))
|
|
57
57
|
end
|
|
@@ -14,16 +14,17 @@ Capybara.add_selector(:button, locator_type: [String, Symbol]) do
|
|
|
14
14
|
XPath.string.n.is(locator) |
|
|
15
15
|
XPath.descendant(:img)[XPath.attr(:alt).is(locator)]
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
label_contains_xpath = locate_label(locator).descendant[labellable_elements]
|
|
18
|
+
input_btn_xpath = input_btn_xpath[locator_matchers]
|
|
19
|
+
btn_xpath = btn_xpath[btn_matchers]
|
|
19
20
|
aria_btn_xpath = aria_btn_xpath[btn_matchers]
|
|
20
21
|
|
|
21
22
|
alt_matches = XPath.attr(:alt).is(locator)
|
|
22
23
|
alt_matches |= XPath.attr(:'aria-label').is(locator) if enable_aria_label
|
|
23
|
-
image_btn_xpath = image_btn_xpath[alt_matches]
|
|
24
|
+
image_btn_xpath = image_btn_xpath[alt_matches]
|
|
24
25
|
end
|
|
25
26
|
|
|
26
|
-
btn_xpaths = [input_btn_xpath, btn_xpath, image_btn_xpath]
|
|
27
|
+
btn_xpaths = [input_btn_xpath, btn_xpath, image_btn_xpath, label_contains_xpath].compact
|
|
27
28
|
btn_xpaths << aria_btn_xpath if enable_aria_role
|
|
28
29
|
|
|
29
30
|
%i[value title type].inject(btn_xpaths.inject(&:union)) do |memo, ef|
|
|
@@ -60,4 +61,8 @@ Capybara.add_selector(:button, locator_type: [String, Symbol]) do
|
|
|
60
61
|
(XPath.attr(config.test_id) == locator if config.test_id)
|
|
61
62
|
].compact.inject(&:|)
|
|
62
63
|
end
|
|
64
|
+
|
|
65
|
+
def labellable_elements
|
|
66
|
+
(XPath.self(:input) & XPath.attr(:type).one_of('submit', 'reset', 'image', 'button')) | XPath.self(:button)
|
|
67
|
+
end
|
|
63
68
|
end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Capybara.add_selector(:checkbox, locator_type: [String, Symbol]) do
|
|
4
4
|
xpath do |locator, allow_self: nil, **options|
|
|
5
|
-
xpath = XPath.axis(allow_self ? :
|
|
5
|
+
xpath = XPath.axis(allow_self ? :'descendant-or-self' : :descendant, :input)[
|
|
6
6
|
XPath.attr(:type) == 'checkbox'
|
|
7
7
|
]
|
|
8
8
|
locate_field(xpath, locator, **options)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Capybara.add_selector(:file_field, locator_type: [String, Symbol]) do
|
|
4
4
|
label 'file field'
|
|
5
5
|
xpath do |locator, allow_self: nil, **options|
|
|
6
|
-
xpath = XPath.axis(allow_self ? :
|
|
6
|
+
xpath = XPath.axis(allow_self ? :'descendant-or-self' : :descendant, :input)[
|
|
7
7
|
XPath.attr(:type) == 'file'
|
|
8
8
|
]
|
|
9
9
|
locate_field(xpath, locator, **options)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Capybara.add_selector(:fillable_field, locator_type: [String, Symbol]) do
|
|
4
4
|
label 'field'
|
|
5
5
|
xpath do |locator, allow_self: nil, **options|
|
|
6
|
-
xpath = XPath.axis(allow_self ? :
|
|
6
|
+
xpath = XPath.axis(allow_self ? :'descendant-or-self' : :descendant, :input, :textarea)[
|
|
7
7
|
!XPath.attr(:type).one_of('submit', 'image', 'radio', 'checkbox', 'hidden', 'file')
|
|
8
8
|
]
|
|
9
9
|
locate_field(xpath, locator, **options)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Capybara.add_selector(:radio_button, locator_type: [String, Symbol]) do
|
|
4
4
|
label 'radio button'
|
|
5
5
|
xpath do |locator, allow_self: nil, **options|
|
|
6
|
-
xpath = XPath.axis(allow_self ? :
|
|
6
|
+
xpath = XPath.axis(allow_self ? :'descendant-or-self' : :descendant, :input)[
|
|
7
7
|
XPath.attr(:type) == 'radio'
|
|
8
8
|
]
|
|
9
9
|
locate_field(xpath, locator, **options)
|
|
@@ -260,7 +260,8 @@ module Capybara
|
|
|
260
260
|
|
|
261
261
|
def parameter_names(block)
|
|
262
262
|
key_types = %i[key keyreq]
|
|
263
|
-
|
|
263
|
+
# user filter_map when we drop dupport for 2.6
|
|
264
|
+
block.parameters.select { |(type, _name)| key_types.include? type }.map { |(_, name)| name }
|
|
264
265
|
end
|
|
265
266
|
|
|
266
267
|
def expression(type, allowed_filters, &block)
|
|
@@ -101,13 +101,11 @@ module Capybara
|
|
|
101
101
|
private
|
|
102
102
|
|
|
103
103
|
def options_with_defaults(options)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
end
|
|
104
|
+
expression_filters.chain(node_filters)
|
|
105
|
+
.select { |_n, filter| filter.default? }
|
|
106
|
+
.each_with_object(options.dup) do |(name, filter), opts|
|
|
107
|
+
opts[name] = filter.default unless opts.key?(name)
|
|
109
108
|
end
|
|
110
|
-
options
|
|
111
109
|
end
|
|
112
110
|
|
|
113
111
|
def add_filter(name, filter_class, *types, matcher: nil, **options, &block)
|
data/lib/capybara/selector.rb
CHANGED
|
@@ -14,6 +14,7 @@ require 'capybara/selector/definition'
|
|
|
14
14
|
# * :left_of (Element) - Match elements left of the passed element on the page
|
|
15
15
|
# * :right_of (Element) - Match elements right of the passed element on the page
|
|
16
16
|
# * :near (Element) - Match elements near (within 50px) the passed element on the page
|
|
17
|
+
# * :focused (Boolean) - Match elements with focus (requires driver support)
|
|
17
18
|
#
|
|
18
19
|
# ### Built-in Selectors
|
|
19
20
|
#
|