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