capybara 3.37.1 → 3.38.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 +34 -4
- data/README.md +23 -11
- data/lib/capybara/helpers.rb +5 -1
- data/lib/capybara/node/base.rb +2 -1
- data/lib/capybara/queries/base_query.rb +2 -2
- data/lib/capybara/queries/selector_query.rb +4 -2
- data/lib/capybara/queries/text_query.rb +1 -1
- data/lib/capybara/rack_test/browser.rb +8 -2
- data/lib/capybara/rack_test/form.rb +29 -7
- data/lib/capybara/registrations/servers.rb +17 -9
- data/lib/capybara/selector/definition.rb +1 -1
- data/lib/capybara/selector/filter_set.rb +4 -5
- data/lib/capybara/selector/regexp_disassembler.rb +2 -5
- data/lib/capybara/selenium/driver.rb +3 -0
- data/lib/capybara/selenium/extensions/html5_drag.rb +2 -4
- data/lib/capybara/selenium/logger_suppressor.rb +4 -0
- data/lib/capybara/selenium/node.rb +52 -15
- data/lib/capybara/selenium/nodes/firefox_node.rb +2 -2
- data/lib/capybara/selenium/nodes/safari_node.rb +2 -2
- data/lib/capybara/server/animation_disabler.rb +20 -20
- data/lib/capybara/server/middleware.rb +1 -1
- data/lib/capybara/session/config.rb +3 -1
- data/lib/capybara/session.rb +11 -9
- data/lib/capybara/spec/session/attach_file_spec.rb +6 -0
- data/lib/capybara/spec/session/check_spec.rb +1 -0
- data/lib/capybara/spec/session/current_scope_spec.rb +1 -1
- data/lib/capybara/spec/session/fill_in_spec.rb +6 -0
- data/lib/capybara/spec/session/find_spec.rb +1 -1
- data/lib/capybara/spec/session/has_ancestor_spec.rb +2 -2
- data/lib/capybara/spec/session/has_button_spec.rb +6 -0
- data/lib/capybara/spec/session/has_link_spec.rb +6 -0
- data/lib/capybara/spec/session/has_select_spec.rb +6 -0
- data/lib/capybara/spec/session/has_text_spec.rb +4 -8
- data/lib/capybara/spec/session/node_spec.rb +24 -1
- data/lib/capybara/spec/session/reset_session_spec.rb +13 -0
- data/lib/capybara/spec/session/within_spec.rb +13 -0
- data/lib/capybara/spec/spec_helper.rb +8 -2
- data/lib/capybara/spec/test_app.rb +25 -6
- data/lib/capybara/spec/views/form.erb +13 -0
- data/lib/capybara/spec/views/with_html.erb +2 -2
- data/lib/capybara/spec/views/with_scope.erb +2 -2
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara.rb +4 -2
- data/spec/capybara_spec.rb +12 -0
- data/spec/counter_spec.rb +35 -0
- data/spec/dsl_spec.rb +2 -0
- data/spec/minitest_spec.rb +4 -0
- data/spec/minitest_spec_spec.rb +4 -0
- data/spec/per_session_config_spec.rb +1 -1
- data/spec/rack_test_spec.rb +8 -0
- data/spec/rspec/shared_spec_matchers.rb +1 -1
- data/spec/rspec_spec.rb +2 -2
- data/spec/selector_spec.rb +2 -2
- data/spec/selenium_spec_chrome.rb +2 -0
- data/spec/selenium_spec_chrome_remote.rb +4 -2
- data/spec/selenium_spec_edge.rb +2 -0
- data/spec/selenium_spec_firefox.rb +11 -5
- data/spec/selenium_spec_firefox_remote.rb +4 -2
- data/spec/selenium_spec_ie.rb +3 -1
- data/spec/selenium_spec_safari.rb +2 -0
- data/spec/shared_selenium_session.rb +3 -4
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 163cb499d23bf17278c9912462fb3fb337b2d28b764a95c5ed3f9c4af3f1c317
|
4
|
+
data.tar.gz: 7010ef4e7f6af6c4d78ae58a05846c2607864d6fe24d05c49747fc1e1ded8675
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 66ac2676f22d83ec1a4478f68a8f7f095c9c9f9396eb1b5c1bf34803e15e19ed02c1f1342731d55787219ddd2d9d0829eb0c76ef702c4e07ac3a0b19a9c2aaec
|
7
|
+
data.tar.gz: f35c37f9ccdcdd58c1c76c80cf58eb8c62b0878bb40ff1dd6191978978409b41adabbc34013868e30a5ec4f33d077532ad8c1a90fdf0138dd524703c5168a001
|
data/History.md
CHANGED
@@ -1,5 +1,35 @@
|
|
1
|
+
# Version 3.38.0
|
2
|
+
Release date: 2022-11-03
|
3
|
+
|
4
|
+
### Changed
|
5
|
+
|
6
|
+
* Capybara.w3c_click_offset now defaults to true. If you need click offsets to be from the elements top left corner set it to false in your config
|
7
|
+
|
8
|
+
### Added
|
9
|
+
|
10
|
+
* Support Selenium 4.3 changes to click offset calculations
|
11
|
+
* `click`, `double_click`, `right_click` can now be called on the session to click the currently scoped element (or document)
|
12
|
+
* `Session#within` now passes the scoped element to the block
|
13
|
+
* Support rack-test 2+
|
14
|
+
* Retry interval is now configurable [Masahiro NOMOTO]
|
15
|
+
* Support Puma 6 - Issue #2590
|
16
|
+
* Selenium: DetachedShadowRootError is treated as an invalid element error [Perryn Fowler]
|
17
|
+
* Selenium: When inspected shadow roots will have a tag name of "ShadowRoot"
|
18
|
+
* `evaluate_async_script` added to Session::DSL_METHODS [Henry Blyth]
|
19
|
+
|
20
|
+
### Fixed
|
21
|
+
|
22
|
+
* Use higher precision clock in Capybara::Helpers::Timer if available
|
23
|
+
* rack-test driver behavior with \r\n - Issue #2547 [Stefan Hoffmann]
|
24
|
+
* Updated for deprecation of positional parameters in Selenium::WebDriver::ActionBuilder#pause
|
25
|
+
* Explicitly set cause on server raised errors
|
26
|
+
* Options no longer duplicated in have_xxx invalid option error message [Yudai Takada]
|
27
|
+
* Animation disabler is now threadsafe [Daniel Sheppard]
|
28
|
+
* Server connection count tracking [Oleksandr K.]
|
29
|
+
* Ensure scopes are reset when session is [Henry Blyth]
|
30
|
+
|
1
31
|
# Version 3.37.1
|
2
|
-
|
32
|
+
Release date: 2022-05-09
|
3
33
|
|
4
34
|
### Fixed
|
5
35
|
|
@@ -810,7 +840,7 @@ Release date: 2018-03-23
|
|
810
840
|
|
811
841
|
### Changed
|
812
842
|
|
813
|
-
*
|
843
|
+
* Visible text whitespace is no longer fully normalized in favor of being more in line with the WebDriver spec for visible text
|
814
844
|
* Drivers are expected to close extra windows when resetting the session
|
815
845
|
* Selenium driver supports Date/Time when filling in date/time/datetime-local inputs
|
816
846
|
* `current_url` returns the url for the top level browsing context
|
@@ -1216,7 +1246,7 @@ Release date: 2016-01-27
|
|
1216
1246
|
|
1217
1247
|
# Version 2.6.0
|
1218
1248
|
|
1219
|
-
|
1249
|
+
Release date: 2016-01-17
|
1220
1250
|
|
1221
1251
|
### Fixed
|
1222
1252
|
|
@@ -1280,7 +1310,7 @@ Release date: 2014-10-13
|
|
1280
1310
|
|
1281
1311
|
# Version 2.4.3
|
1282
1312
|
|
1283
|
-
|
1313
|
+
Release date: 2014-09-21
|
1284
1314
|
|
1285
1315
|
### Fixed
|
1286
1316
|
|
data/README.md
CHANGED
@@ -161,7 +161,7 @@ You can now write your specs like so:
|
|
161
161
|
```ruby
|
162
162
|
describe "the signin process", type: :feature do
|
163
163
|
before :each do
|
164
|
-
User.
|
164
|
+
User.create(email: 'user@example.com', password: 'password')
|
165
165
|
end
|
166
166
|
|
167
167
|
it "signs me in" do
|
@@ -192,7 +192,7 @@ Capybara also comes with a built in DSL for creating descriptive acceptance test
|
|
192
192
|
```ruby
|
193
193
|
feature "Signing in" do
|
194
194
|
background do
|
195
|
-
User.
|
195
|
+
User.create(email: 'user@example.com', password: 'caplin')
|
196
196
|
end
|
197
197
|
|
198
198
|
scenario "Signing in with correct credentials" do
|
@@ -205,7 +205,7 @@ feature "Signing in" do
|
|
205
205
|
expect(page).to have_content 'Success'
|
206
206
|
end
|
207
207
|
|
208
|
-
given(:other_user) { User.
|
208
|
+
given(:other_user) { User.create(email: 'other@example.com', password: 'rous') }
|
209
209
|
|
210
210
|
scenario "Signing in as another user" do
|
211
211
|
visit '/sessions/new'
|
@@ -336,7 +336,7 @@ By default, Capybara uses the `:rack_test` driver, which is fast but limited: it
|
|
336
336
|
does not support JavaScript, nor is it able to access HTTP resources outside of
|
337
337
|
your Rack application, such as remote APIs and OAuth services. To get around
|
338
338
|
these limitations, you can set up a different default driver for your features.
|
339
|
-
For example if you'd prefer to run everything in Selenium, you could do:
|
339
|
+
For example, if you'd prefer to run everything in Selenium, you could do:
|
340
340
|
|
341
341
|
```ruby
|
342
342
|
Capybara.default_driver = :selenium # :selenium_chrome and :selenium_chrome_headless are also registered
|
@@ -630,7 +630,7 @@ JS
|
|
630
630
|
|
631
631
|
### <a name="modals"></a>Modals
|
632
632
|
|
633
|
-
In drivers which support it, you can accept, dismiss and respond to alerts, confirms and prompts.
|
633
|
+
In drivers which support it, you can accept, dismiss and respond to alerts, confirms, and prompts.
|
634
634
|
|
635
635
|
You can accept or dismiss alert messages by wrapping the code that produces an alert in a block:
|
636
636
|
|
@@ -781,7 +781,7 @@ expect(page).to have_content('baz')
|
|
781
781
|
If clicking on the *foo* link triggers an asynchronous process, such as
|
782
782
|
an Ajax request, which, when complete will add the *bar* link to the page,
|
783
783
|
clicking on the *bar* link would be expected to fail, since that link doesn't
|
784
|
-
exist yet. However Capybara is smart enough to retry finding the link for a
|
784
|
+
exist yet. However, Capybara is smart enough to retry finding the link for a
|
785
785
|
brief period of time before giving up and throwing an error. The same is true of
|
786
786
|
the next line, which looks for the content *baz* on the page; it will retry
|
787
787
|
looking for that content for a brief time. You can adjust how long this period
|
@@ -795,13 +795,25 @@ Be aware that because of this behaviour, the following two statements are **not*
|
|
795
795
|
equivalent, and you should **always** use the latter!
|
796
796
|
|
797
797
|
```ruby
|
798
|
-
|
799
|
-
|
798
|
+
# Given use of a driver where the page is loaded when visit returns
|
799
|
+
# and that Capybara.predicates_wait is `true`
|
800
|
+
# consider a page where the `a` tag is removed through AJAX after 1s
|
801
|
+
visit(some_path)
|
802
|
+
!page.has_xpath?('a') # is false
|
803
|
+
page.has_no_xpath?('a') # is true
|
800
804
|
```
|
801
805
|
|
802
|
-
|
803
|
-
|
804
|
-
|
806
|
+
First expression:
|
807
|
+
- `has_xpath?('a')` is called right after `visit` returns. It is `true` because the link has not yet been removed
|
808
|
+
- Capybara does not wait upon successful predicates/assertions, therefore **has_xpath? returns `true` immediately**
|
809
|
+
- The expression returns `false` (because it is negated with the leading `!`)
|
810
|
+
|
811
|
+
Second expression:
|
812
|
+
- `has_no_xpath?('a')` is called right after `visit` returns. It is `false` because the link has not yet been removed.
|
813
|
+
- Capybara waits upon failed predicates/assertions, therefore **has_no_xpath? does not return `false` immediately**
|
814
|
+
- Capybara will periodically re-check the predicate/assertion up to the `default_max_wait_time` defined
|
815
|
+
- after 1s, the predicate becomes `true` (because the link has been removed)
|
816
|
+
- The expression returns `true`
|
805
817
|
|
806
818
|
Capybara's RSpec matchers, however, are smart enough to handle either form.
|
807
819
|
The two following statements are functionally equivalent:
|
data/lib/capybara/helpers.rb
CHANGED
@@ -85,7 +85,11 @@ module Capybara
|
|
85
85
|
Kernel.warn(message, uplevel: uplevel)
|
86
86
|
end
|
87
87
|
|
88
|
-
if defined?(Process::
|
88
|
+
if defined?(Process::CLOCK_MONOTONIC_RAW)
|
89
|
+
def monotonic_time; Process.clock_gettime Process::CLOCK_MONOTONIC_RAW; end
|
90
|
+
elsif defined?(Process::CLOCK_MONOTONIC_PRECISE)
|
91
|
+
def monotonic_time; Process.clock_gettime Process::CLOCK_MONOTONIC_PRECISE; end
|
92
|
+
elsif defined?(Process::CLOCK_MONOTONIC)
|
89
93
|
def monotonic_time; Process.clock_gettime Process::CLOCK_MONOTONIC; end
|
90
94
|
else
|
91
95
|
def monotonic_time; Time.now.to_f; end
|
data/lib/capybara/node/base.rb
CHANGED
@@ -77,6 +77,7 @@ module Capybara
|
|
77
77
|
return yield if session.synchronized
|
78
78
|
|
79
79
|
seconds = session_options.default_max_wait_time if [nil, true].include? seconds
|
80
|
+
interval = session_options.default_retry_interval
|
80
81
|
session.synchronized = true
|
81
82
|
timer = Capybara::Helpers.timer(expire_in: seconds)
|
82
83
|
begin
|
@@ -88,7 +89,7 @@ module Capybara
|
|
88
89
|
if driver.wait?
|
89
90
|
raise e if timer.expired?
|
90
91
|
|
91
|
-
sleep
|
92
|
+
sleep interval
|
92
93
|
reload if session_options.automatic_reload
|
93
94
|
else
|
94
95
|
old_base = @base
|
@@ -79,8 +79,8 @@ module Capybara
|
|
79
79
|
if count
|
80
80
|
message << " #{occurrences count}"
|
81
81
|
elsif between
|
82
|
-
message << " between #{between.begin ? between.first : 1} and" \
|
83
|
-
"
|
82
|
+
message << " between #{between.begin ? between.first : 1} and " \
|
83
|
+
"#{between.end ? between.last : 'infinite'} times"
|
84
84
|
elsif maximum
|
85
85
|
message << " at most #{occurrences maximum}"
|
86
86
|
elsif minimum
|
@@ -272,7 +272,7 @@ module Capybara
|
|
272
272
|
end
|
273
273
|
|
274
274
|
def valid_keys
|
275
|
-
VALID_KEYS + custom_keys
|
275
|
+
(VALID_KEYS + custom_keys).uniq
|
276
276
|
end
|
277
277
|
|
278
278
|
def matches_node_filters?(node, errors)
|
@@ -570,7 +570,9 @@ module Capybara
|
|
570
570
|
when :visible
|
571
571
|
node.initial_cache[:visible] || (node.initial_cache[:visible].nil? && node.visible?)
|
572
572
|
when :hidden
|
573
|
-
|
573
|
+
# TODO: check why the 'visbile' cache spelling mistake wasn't caught in a test
|
574
|
+
# (node.initial_cache[:visible] == false) || (node.initial_cache[:visbile].nil? && !node.visible?)
|
575
|
+
(node.initial_cache[:visible] == false) || (node.initial_cache[:visible].nil? && !node.visible?)
|
574
576
|
else
|
575
577
|
true
|
576
578
|
end
|
@@ -13,7 +13,7 @@ module Capybara
|
|
13
13
|
self.session_options = session_options
|
14
14
|
|
15
15
|
if expected_text.nil? && !exact?
|
16
|
-
warn 'Checking for expected text of nil is confusing and/or pointless since it will always match. '\
|
16
|
+
warn 'Checking for expected text of nil is confusing and/or pointless since it will always match. ' \
|
17
17
|
"Please specify a string or regexp instead. #{Capybara::Helpers.filter_backtrace(caller)}"
|
18
18
|
end
|
19
19
|
|
@@ -31,11 +31,17 @@ class Capybara::RackTest::Browser
|
|
31
31
|
request(last_request.fullpath, last_request.env)
|
32
32
|
end
|
33
33
|
|
34
|
-
def submit(method, path, attributes)
|
34
|
+
def submit(method, path, attributes, content_type: nil)
|
35
35
|
path = request_path if path.nil? || path.empty?
|
36
36
|
uri = build_uri(path)
|
37
37
|
uri.query = '' if method.to_s.casecmp('get').zero?
|
38
|
-
process_and_follow_redirects(
|
38
|
+
process_and_follow_redirects(
|
39
|
+
method,
|
40
|
+
uri.to_s,
|
41
|
+
attributes,
|
42
|
+
'HTTP_REFERER' => referer_url,
|
43
|
+
'CONTENT_TYPE' => content_type
|
44
|
+
)
|
39
45
|
end
|
40
46
|
|
41
47
|
def follow(method, path, **attributes)
|
@@ -16,6 +16,8 @@ class Capybara::RackTest::Form < Capybara::RackTest::Node
|
|
16
16
|
def path; @empty_file.path; end
|
17
17
|
def size; 0; end
|
18
18
|
def read; ''; end
|
19
|
+
def append_to(_); end
|
20
|
+
def set_encoding(_); end # rubocop:disable Naming/AccessorMethodName
|
19
21
|
end
|
20
22
|
|
21
23
|
def params(button)
|
@@ -28,19 +30,31 @@ class Capybara::RackTest::Form < Capybara::RackTest::Node
|
|
28
30
|
|
29
31
|
form_elements = native.xpath(form_elements_xpath).reject { |el| submitter?(el) && (el != button.native) }
|
30
32
|
|
31
|
-
form_elements.each_with_object(
|
33
|
+
form_params = form_elements.each_with_object({}.compare_by_identity) do |field, params|
|
32
34
|
case field.name
|
33
35
|
when 'input', 'button' then add_input_param(field, params)
|
34
36
|
when 'select' then add_select_param(field, params)
|
35
37
|
when 'textarea' then add_textarea_param(field, params)
|
36
38
|
end
|
39
|
+
end
|
40
|
+
|
41
|
+
form_params.each_with_object(make_params) do |(name, value), params|
|
42
|
+
merge_param!(params, name, value)
|
37
43
|
end.to_params_hash
|
44
|
+
|
45
|
+
# form_elements.each_with_object(make_params) do |field, params|
|
46
|
+
# case field.name
|
47
|
+
# when 'input', 'button' then add_input_param(field, params)
|
48
|
+
# when 'select' then add_select_param(field, params)
|
49
|
+
# when 'textarea' then add_textarea_param(field, params)
|
50
|
+
# end
|
51
|
+
# end.to_params_hash
|
38
52
|
end
|
39
53
|
|
40
54
|
def submit(button)
|
41
55
|
action = button&.[]('formaction') || native['action']
|
42
56
|
method = button&.[]('formmethod') || request_method
|
43
|
-
driver.submit(method, action.to_s, params(button))
|
57
|
+
driver.submit(method, action.to_s, params(button), content_type: native['enctype'])
|
44
58
|
end
|
45
59
|
|
46
60
|
def multipart?
|
@@ -86,6 +100,8 @@ private
|
|
86
100
|
|
87
101
|
Capybara::RackTest::Node.new(driver, field).value.to_s
|
88
102
|
when 'file'
|
103
|
+
return if value.empty? && params.keys.include?(name) && Rack::Test::VERSION.to_f >= 2.0 # rubocop:disable Performance/InefficientHashSearch
|
104
|
+
|
89
105
|
if multipart?
|
90
106
|
file_to_upload(value)
|
91
107
|
else
|
@@ -94,7 +110,8 @@ private
|
|
94
110
|
else
|
95
111
|
value
|
96
112
|
end
|
97
|
-
merge_param!(params, name, value)
|
113
|
+
# merge_param!(params, name, value)
|
114
|
+
params[name] = value
|
98
115
|
end
|
99
116
|
|
100
117
|
def file_to_upload(filename)
|
@@ -107,18 +124,23 @@ private
|
|
107
124
|
end
|
108
125
|
|
109
126
|
def add_select_param(field, params)
|
127
|
+
name = field['name']
|
110
128
|
if field.has_attribute?('multiple')
|
111
|
-
field.xpath('.//option[@selected]').
|
112
|
-
merge_param!(params, field['name'], (option['value'] || option.text).to_s)
|
129
|
+
value = field.xpath('.//option[@selected]').map do |option|
|
130
|
+
# merge_param!(params, field['name'], (option['value'] || option.text).to_s)
|
131
|
+
(option['value'] || option.text).to_s
|
113
132
|
end
|
133
|
+
params[name] = value unless value.empty?
|
114
134
|
else
|
115
135
|
option = field.xpath('.//option[@selected]').first || field.xpath('.//option').first
|
116
|
-
merge_param!(params, field['name'], (option['value'] || option.text).to_s) if option
|
136
|
+
# merge_param!(params, field['name'], (option['value'] || option.text).to_s) if option
|
137
|
+
params[name] = (option['value'] || option.text).to_s if option
|
117
138
|
end
|
118
139
|
end
|
119
140
|
|
120
141
|
def add_textarea_param(field, params)
|
121
|
-
merge_param!(params, field['name'], field['_capybara_raw_value'].to_s.gsub(/\n/, "\r\n"))
|
142
|
+
# merge_param!(params, field['name'], field['_capybara_raw_value'].to_s.gsub(/\r?\n/, "\r\n"))
|
143
|
+
params[field['name']] = field['_capybara_raw_value'].to_s.gsub(/\r?\n/, "\r\n")
|
122
144
|
end
|
123
145
|
|
124
146
|
def submitter?(el)
|
@@ -10,7 +10,7 @@ Capybara.register_server :webrick do |app, port, host, **options|
|
|
10
10
|
Rack::Handler::WEBrick.run(app, **options)
|
11
11
|
end
|
12
12
|
|
13
|
-
Capybara.register_server :puma do |app, port, host, **options|
|
13
|
+
Capybara.register_server :puma do |app, port, host, **options| # rubocop:disable Metrics/BlockLength
|
14
14
|
begin
|
15
15
|
require 'rack/handler/puma'
|
16
16
|
rescue LoadError
|
@@ -29,17 +29,25 @@ Capybara.register_server :puma do |app, port, host, **options|
|
|
29
29
|
|
30
30
|
conf = Rack::Handler::Puma.config(app, options)
|
31
31
|
conf.clamp
|
32
|
-
events = conf.options[:Silent] ? ::Puma::Events.strings : ::Puma::Events.stdio
|
33
32
|
|
34
33
|
puma_ver = Gem::Version.new(Puma::Const::PUMA_VERSION)
|
35
34
|
require_relative 'patches/puma_ssl' if Gem::Requirement.new('>=4.0.0', '< 4.1.0').satisfied_by?(puma_ver)
|
36
35
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
36
|
+
logger = (defined?(::Puma::LogWriter) ? ::Puma::LogWriter : ::Puma::Events).then do |cls|
|
37
|
+
conf.options[:Silent] ? cls.strings : cls.stdio
|
38
|
+
end
|
39
|
+
conf.options[:log_writer] = logger
|
40
|
+
|
41
|
+
logger.log 'Capybara starting Puma...'
|
42
|
+
logger.log "* Version #{Puma::Const::PUMA_VERSION} , codename: #{Puma::Const::CODE_NAME}"
|
43
|
+
logger.log "* Min threads: #{conf.options[:min_threads]}, max threads: #{conf.options[:max_threads]}"
|
44
|
+
|
45
|
+
Puma::Server.new(
|
46
|
+
conf.app,
|
47
|
+
defined?(::Puma::LogWriter) ? nil : logger,
|
48
|
+
conf.options
|
49
|
+
).tap do |s|
|
50
|
+
s.binder.parse conf.options[:binds], (s.log_writer rescue s.events) # rubocop:disable Style/RescueModifier
|
51
|
+
s.min_threads, s.max_threads = conf.options[:min_threads], conf.options[:max_threads] if s.respond_to? :min_threads=
|
44
52
|
end.run.join
|
45
53
|
end
|
@@ -203,7 +203,7 @@ module Capybara
|
|
203
203
|
|
204
204
|
##
|
205
205
|
#
|
206
|
-
# Set the default visibility mode that
|
206
|
+
# Set the default visibility mode that should be used if no visible option is passed when using the selector.
|
207
207
|
# If not specified will default to the behavior indicated by Capybara.ignore_hidden_elements
|
208
208
|
#
|
209
209
|
# @param [Symbol] default_visibility Only find elements with the specified visibility:
|
@@ -101,11 +101,10 @@ module Capybara
|
|
101
101
|
private
|
102
102
|
|
103
103
|
def options_with_defaults(options)
|
104
|
-
expression_filters
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
end
|
104
|
+
expression_filters
|
105
|
+
.chain(node_filters)
|
106
|
+
.filter_map { |name, filter| [name, filter.default] if filter.default? }
|
107
|
+
.to_h.merge!(options)
|
109
108
|
end
|
110
109
|
|
111
110
|
def add_filter(name, filter_class, *types, matcher: nil, **options, &block)
|
@@ -69,11 +69,8 @@ module Capybara
|
|
69
69
|
suffixes = [[]]
|
70
70
|
strs.reverse_each do |str|
|
71
71
|
if str.is_a? Set
|
72
|
-
prefixes = str.
|
73
|
-
|
74
|
-
result = []
|
75
|
-
prefixes.product(suffixes) { |pair| result << pair.flatten(1) }
|
76
|
-
suffixes = result
|
72
|
+
prefixes = str.flat_map { |s| combine(s) }
|
73
|
+
suffixes = prefixes.product(suffixes).map { |pair| pair.flatten(1) }
|
77
74
|
else
|
78
75
|
suffixes.each { |arr| arr.unshift str }
|
79
76
|
end
|
@@ -323,6 +323,9 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
323
323
|
]
|
324
324
|
end
|
325
325
|
end
|
326
|
+
if defined?(::Selenium::WebDriver::Error::DetachedShadowRootError)
|
327
|
+
errors.concat([::Selenium::WebDriver::Error::DetachedShadowRootError])
|
328
|
+
end
|
326
329
|
end
|
327
330
|
end
|
328
331
|
|
@@ -39,10 +39,8 @@ class Capybara::Selenium::Node
|
|
39
39
|
input.set_file(args)
|
40
40
|
driver.execute_script DROP_FILE, self, input
|
41
41
|
else
|
42
|
-
items = args.
|
43
|
-
arg.
|
44
|
-
arr_ << { type: type, data: data }
|
45
|
-
end
|
42
|
+
items = args.flat_map do |arg|
|
43
|
+
arg.map { |(type, data)| { type: type, data: data } }
|
46
44
|
end
|
47
45
|
driver.execute_script DROP_STRING, items, self
|
48
46
|
end
|
@@ -10,6 +10,8 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
10
10
|
include Capybara::Selenium::Scroll
|
11
11
|
|
12
12
|
def visible_text
|
13
|
+
raise NotImplementedError, 'Getting visible text is not currently supported directly on shadow roots' if shadow_root?
|
14
|
+
|
13
15
|
native.text
|
14
16
|
end
|
15
17
|
|
@@ -37,9 +39,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
37
39
|
end
|
38
40
|
|
39
41
|
def style(styles)
|
40
|
-
styles.
|
41
|
-
result[style] = native.css_value(style)
|
42
|
-
end
|
42
|
+
styles.to_h { |style| [style, native.css_value(style)] }
|
43
43
|
end
|
44
44
|
|
45
45
|
##
|
@@ -115,11 +115,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
115
115
|
action.click(target)
|
116
116
|
else
|
117
117
|
action.click_and_hold(target)
|
118
|
-
|
119
|
-
action.pause(action.pointer_inputs.first, click_options.delay)
|
120
|
-
else
|
121
|
-
action.pause(click_options.delay)
|
122
|
-
end
|
118
|
+
action_pause(action, click_options.delay)
|
123
119
|
action.release
|
124
120
|
end
|
125
121
|
end
|
@@ -140,9 +136,9 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
140
136
|
action.context_click(target)
|
141
137
|
elsif w3c?
|
142
138
|
action.move_to(target) if target
|
143
|
-
action.pointer_down(:right)
|
144
|
-
|
145
|
-
|
139
|
+
action.pointer_down(:right).then do |act|
|
140
|
+
action_pause(act, click_options.delay)
|
141
|
+
end.pointer_up(:right)
|
146
142
|
else
|
147
143
|
raise ArgumentError, 'Delay is not supported when right clicking with legacy (non-w3c) selenium driver'
|
148
144
|
end
|
@@ -184,7 +180,12 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
184
180
|
end
|
185
181
|
|
186
182
|
def tag_name
|
187
|
-
@tag_name ||=
|
183
|
+
@tag_name ||=
|
184
|
+
if native.respond_to? :tag_name
|
185
|
+
native.tag_name.downcase
|
186
|
+
else
|
187
|
+
shadow_root? ? 'ShadowRoot' : 'Unknown'
|
188
|
+
end
|
188
189
|
end
|
189
190
|
|
190
191
|
def visible?; boolean_attr(native.displayed?); end
|
@@ -415,10 +416,30 @@ private
|
|
415
416
|
|
416
417
|
def action_with_modifiers(click_options)
|
417
418
|
actions = browser_action.tap do |acts|
|
418
|
-
if click_options.
|
419
|
-
|
419
|
+
if click_options.coords?
|
420
|
+
if click_options.center_offset?
|
421
|
+
if Selenium::WebDriver::VERSION.to_f >= 4.3
|
422
|
+
acts.move_to(native, *click_options.coords)
|
423
|
+
else
|
424
|
+
::Selenium::WebDriver.logger.suppress_deprecations do
|
425
|
+
acts.move_to(native).move_by(*click_options.coords)
|
426
|
+
end
|
427
|
+
end
|
428
|
+
elsif Selenium::WebDriver::VERSION.to_f >= 4.3
|
429
|
+
right_by, down_by = *click_options.coords
|
430
|
+
size = native.size
|
431
|
+
left_offset = (size[:width] / 2).to_i
|
432
|
+
top_offset = (size[:height] / 2).to_i
|
433
|
+
left = -left_offset + right_by
|
434
|
+
top = -top_offset + down_by
|
435
|
+
acts.move_to(native, left, top)
|
436
|
+
else
|
437
|
+
::Selenium::WebDriver.logger.suppress_deprecations do
|
438
|
+
acts.move_to(native, *click_options.coords)
|
439
|
+
end
|
440
|
+
end
|
420
441
|
else
|
421
|
-
acts.move_to(native
|
442
|
+
acts.move_to(native)
|
422
443
|
end
|
423
444
|
end
|
424
445
|
modifiers_down(actions, click_options.keys)
|
@@ -461,6 +482,18 @@ private
|
|
461
482
|
capabilities.is_a?(::Selenium::WebDriver::Remote::W3C::Capabilities)
|
462
483
|
end
|
463
484
|
|
485
|
+
def action_pause(action, duration)
|
486
|
+
if w3c?
|
487
|
+
if Selenium::WebDriver::VERSION.to_f >= 4.2
|
488
|
+
action.pause(device: action.pointer_inputs.first, duration: duration)
|
489
|
+
else
|
490
|
+
action.pause(action.pointer_inputs.first, duration)
|
491
|
+
end
|
492
|
+
else
|
493
|
+
action.pause(duration)
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
464
497
|
def normalize_keys(keys)
|
465
498
|
keys.map do |key|
|
466
499
|
case key
|
@@ -502,6 +535,10 @@ private
|
|
502
535
|
id || type_or_id
|
503
536
|
end
|
504
537
|
|
538
|
+
def shadow_root?
|
539
|
+
defined?(::Selenium::WebDriver::ShadowRoot) && native.is_a?(::Selenium::WebDriver::ShadowRoot)
|
540
|
+
end
|
541
|
+
|
505
542
|
GET_XPATH_SCRIPT = <<~'JS'
|
506
543
|
(function(el, xml){
|
507
544
|
var xpath = '';
|
@@ -11,8 +11,8 @@ class Capybara::Selenium::FirefoxNode < Capybara::Selenium::Node
|
|
11
11
|
super
|
12
12
|
rescue ::Selenium::WebDriver::Error::ElementNotInteractableError
|
13
13
|
if tag_name == 'tr'
|
14
|
-
warn 'You are attempting to click a table row which has issues in geckodriver/marionette - '\
|
15
|
-
'see https://github.com/mozilla/geckodriver/issues/1228
|
14
|
+
warn 'You are attempting to click a table row which has issues in geckodriver/marionette - ' \
|
15
|
+
'see https://github.com/mozilla/geckodriver/issues/1228 - Your test should probably be ' \
|
16
16
|
'clicking on a table cell like a user would. Clicking the first cell in the row instead.'
|
17
17
|
return find_css('th:first-child,td:first-child')[0].click(keys, **options)
|
18
18
|
end
|
@@ -11,8 +11,8 @@ class Capybara::Selenium::SafariNode < Capybara::Selenium::Node
|
|
11
11
|
super
|
12
12
|
rescue ::Selenium::WebDriver::Error::ElementNotInteractableError
|
13
13
|
if tag_name == 'tr'
|
14
|
-
warn 'You are attempting to click a table row which has issues in safaridriver - '\
|
15
|
-
'Your test should probably be clicking on a table cell like a user would. '\
|
14
|
+
warn 'You are attempting to click a table row which has issues in safaridriver - ' \
|
15
|
+
'Your test should probably be clicking on a table cell like a user would. ' \
|
16
16
|
'Clicking the first cell in the row instead.'
|
17
17
|
return find_css('th:first-child,td:first-child')[0].click(keys, **options)
|
18
18
|
end
|