capybara 3.37.0 → 3.38.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +40 -3
  3. data/README.md +23 -11
  4. data/lib/capybara/helpers.rb +6 -2
  5. data/lib/capybara/node/base.rb +2 -1
  6. data/lib/capybara/node/finders.rb +7 -0
  7. data/lib/capybara/queries/base_query.rb +2 -2
  8. data/lib/capybara/queries/selector_query.rb +4 -2
  9. data/lib/capybara/queries/text_query.rb +1 -1
  10. data/lib/capybara/rack_test/browser.rb +23 -3
  11. data/lib/capybara/rack_test/form.rb +29 -7
  12. data/lib/capybara/registrations/servers.rb +17 -9
  13. data/lib/capybara/selector/definition.rb +1 -1
  14. data/lib/capybara/selector/filter_set.rb +4 -5
  15. data/lib/capybara/selector/regexp_disassembler.rb +2 -5
  16. data/lib/capybara/selector/selector.rb +5 -1
  17. data/lib/capybara/selenium/driver.rb +3 -0
  18. data/lib/capybara/selenium/extensions/html5_drag.rb +2 -4
  19. data/lib/capybara/selenium/logger_suppressor.rb +4 -0
  20. data/lib/capybara/selenium/node.rb +52 -15
  21. data/lib/capybara/selenium/nodes/firefox_node.rb +2 -2
  22. data/lib/capybara/selenium/nodes/safari_node.rb +2 -2
  23. data/lib/capybara/server/animation_disabler.rb +20 -20
  24. data/lib/capybara/server/middleware.rb +1 -1
  25. data/lib/capybara/session/config.rb +3 -1
  26. data/lib/capybara/session.rb +11 -9
  27. data/lib/capybara/spec/session/attach_file_spec.rb +6 -0
  28. data/lib/capybara/spec/session/check_spec.rb +1 -0
  29. data/lib/capybara/spec/session/current_scope_spec.rb +1 -1
  30. data/lib/capybara/spec/session/fill_in_spec.rb +6 -0
  31. data/lib/capybara/spec/session/find_spec.rb +6 -0
  32. data/lib/capybara/spec/session/has_ancestor_spec.rb +2 -2
  33. data/lib/capybara/spec/session/has_button_spec.rb +6 -0
  34. data/lib/capybara/spec/session/has_link_spec.rb +12 -0
  35. data/lib/capybara/spec/session/has_select_spec.rb +6 -0
  36. data/lib/capybara/spec/session/has_text_spec.rb +4 -8
  37. data/lib/capybara/spec/session/node_spec.rb +24 -1
  38. data/lib/capybara/spec/session/reset_session_spec.rb +13 -0
  39. data/lib/capybara/spec/session/visit_spec.rb +6 -0
  40. data/lib/capybara/spec/session/within_spec.rb +13 -0
  41. data/lib/capybara/spec/spec_helper.rb +8 -2
  42. data/lib/capybara/spec/test_app.rb +41 -6
  43. data/lib/capybara/spec/views/form.erb +13 -0
  44. data/lib/capybara/spec/views/with_html.erb +2 -2
  45. data/lib/capybara/spec/views/with_scope.erb +2 -2
  46. data/lib/capybara/version.rb +1 -1
  47. data/lib/capybara.rb +4 -2
  48. data/spec/capybara_spec.rb +12 -0
  49. data/spec/counter_spec.rb +35 -0
  50. data/spec/dsl_spec.rb +2 -0
  51. data/spec/minitest_spec.rb +4 -0
  52. data/spec/minitest_spec_spec.rb +4 -0
  53. data/spec/per_session_config_spec.rb +1 -1
  54. data/spec/rack_test_spec.rb +8 -0
  55. data/spec/rspec/shared_spec_matchers.rb +1 -1
  56. data/spec/rspec_spec.rb +2 -2
  57. data/spec/selector_spec.rb +3 -3
  58. data/spec/selenium_spec_chrome.rb +2 -0
  59. data/spec/selenium_spec_chrome_remote.rb +4 -2
  60. data/spec/selenium_spec_edge.rb +2 -0
  61. data/spec/selenium_spec_firefox.rb +11 -5
  62. data/spec/selenium_spec_firefox_remote.rb +4 -2
  63. data/spec/selenium_spec_ie.rb +3 -1
  64. data/spec/selenium_spec_safari.rb +2 -0
  65. data/spec/shared_selenium_session.rb +3 -4
  66. metadata +3 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 125837a4db6ae6c9b12ab2339f5daa673397ef9cdb9054689fb1d155a5d6e62f
4
- data.tar.gz: 445b064e8ae642678457cbf2739a1a6124b7d35dd6aaf295d3d9edce4f6ee8f3
3
+ metadata.gz: 163cb499d23bf17278c9912462fb3fb337b2d28b764a95c5ed3f9c4af3f1c317
4
+ data.tar.gz: 7010ef4e7f6af6c4d78ae58a05846c2607864d6fe24d05c49747fc1e1ded8675
5
5
  SHA512:
6
- metadata.gz: 95154994ad904eae4c9eeb73605bcc273daef240a0cbf241afa8cf1bcfef1ca2de52ecd159fe551ee2d863a0a9a07a2fb9a3beab659f334ec5782f50c5af4ba1
7
- data.tar.gz: 600e6610c5c48ac5619d1c4cf2ddfc83b86d5315ea259c7d8a6476863a14d4a209bcec0c6334dab5f3ebefac5eaeb1ccaa8a263114a5838f7a49a8483fb44538
6
+ metadata.gz: 66ac2676f22d83ec1a4478f68a8f7f095c9c9f9396eb1b5c1bf34803e15e19ed02c1f1342731d55787219ddd2d9d0829eb0c76ef702c4e07ac3a0b19a9c2aaec
7
+ data.tar.gz: f35c37f9ccdcdd58c1c76c80cf58eb8c62b0878bb40ff1dd6191978978409b41adabbc34013868e30a5ec4f33d077532ad8c1a90fdf0138dd524703c5168a001
data/History.md CHANGED
@@ -1,3 +1,40 @@
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
+
31
+ # Version 3.37.1
32
+ Release date: 2022-05-09
33
+
34
+ ### Fixed
35
+
36
+ * Regression in rack-test visit - Issue #2548
37
+
1
38
  # Version 3.37.0
2
39
  Release date: 2022-05-07
3
40
 
@@ -803,7 +840,7 @@ Release date: 2018-03-23
803
840
 
804
841
  ### Changed
805
842
 
806
- * Visibile text whitespace is no longer fully normalized in favor of being more in line with the WebDriver spec for visible text
843
+ * Visible text whitespace is no longer fully normalized in favor of being more in line with the WebDriver spec for visible text
807
844
  * Drivers are expected to close extra windows when resetting the session
808
845
  * Selenium driver supports Date/Time when filling in date/time/datetime-local inputs
809
846
  * `current_url` returns the url for the top level browsing context
@@ -1209,7 +1246,7 @@ Release date: 2016-01-27
1209
1246
 
1210
1247
  # Version 2.6.0
1211
1248
 
1212
- Relase date: 2016-01-17
1249
+ Release date: 2016-01-17
1213
1250
 
1214
1251
  ### Fixed
1215
1252
 
@@ -1273,7 +1310,7 @@ Release date: 2014-10-13
1273
1310
 
1274
1311
  # Version 2.4.3
1275
1312
 
1276
- Relase date: 2014-09-21
1313
+ Release date: 2014-09-21
1277
1314
 
1278
1315
  ### Fixed
1279
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.make(email: 'user@example.com', password: 'password')
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.make(email: 'user@example.com', password: 'caplin')
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.make(email: 'other@example.com', password: 'rous') }
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
- !page.has_xpath?('a')
799
- page.has_no_xpath?('a')
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
- The former would immediately fail because the content has not yet been removed.
803
- Only the latter would wait for the asynchronous process to remove the content
804
- from the page.
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:
@@ -73,7 +73,7 @@ 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
78
  new_trace = trace.grep_v(filter) if new_trace.empty?
79
79
  new_trace = trace.dup if new_trace.empty?
@@ -85,7 +85,11 @@ module Capybara
85
85
  Kernel.warn(message, uplevel: uplevel)
86
86
  end
87
87
 
88
- if defined?(Process::CLOCK_MONOTONIC)
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
@@ -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(0.01)
92
+ sleep interval
92
93
  reload if session_options.automatic_reload
93
94
  else
94
95
  old_base = @base
@@ -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
 
@@ -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
- " #{between.end ? between.last : 'infinite'} times"
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
- (node.initial_cache[:visible] == false) || (node.initial_cache[:visbile].nil? && !node.visible?)
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
 
@@ -20,6 +20,8 @@ class Capybara::RackTest::Browser
20
20
  end
21
21
 
22
22
  def visit(path, **attributes)
23
+ @new_visit_request = true
24
+ reset_cache!
23
25
  reset_host!
24
26
  process_and_follow_redirects(:get, path, attributes)
25
27
  end
@@ -29,11 +31,17 @@ class Capybara::RackTest::Browser
29
31
  request(last_request.fullpath, last_request.env)
30
32
  end
31
33
 
32
- def submit(method, path, attributes)
34
+ def submit(method, path, attributes, content_type: nil)
33
35
  path = request_path if path.nil? || path.empty?
34
36
  uri = build_uri(path)
35
37
  uri.query = '' if method.to_s.casecmp('get').zero?
36
- process_and_follow_redirects(method, uri.to_s, attributes, 'HTTP_REFERER' => referer_url)
38
+ process_and_follow_redirects(
39
+ method,
40
+ uri.to_s,
41
+ attributes,
42
+ 'HTTP_REFERER' => referer_url,
43
+ 'CONTENT_TYPE' => content_type
44
+ )
37
45
  end
38
46
 
39
47
  def follow(method, path, **attributes)
@@ -45,7 +53,6 @@ class Capybara::RackTest::Browser
45
53
  def process_and_follow_redirects(method, path, attributes = {}, env = {})
46
54
  @current_fragment = build_uri(path).fragment
47
55
  process(method, path, attributes, env)
48
-
49
56
  return unless driver.follow_redirects?
50
57
 
51
58
  driver.redirect_limit.times do
@@ -69,6 +76,7 @@ class Capybara::RackTest::Browser
69
76
  @current_scheme, @current_host, @current_port = new_uri.select(:scheme, :host, :port)
70
77
  @current_fragment = new_uri.fragment || @current_fragment
71
78
  reset_cache!
79
+ @new_visit_request = false
72
80
  send(method, new_uri.to_s, attributes, env.merge(options[:headers] || {}))
73
81
  end
74
82
 
@@ -127,6 +135,18 @@ class Capybara::RackTest::Browser
127
135
  dom.title
128
136
  end
129
137
 
138
+ def last_request
139
+ raise Rack::Test::Error if @new_visit_request
140
+
141
+ super
142
+ end
143
+
144
+ def last_response
145
+ raise Rack::Test::Error if @new_visit_request
146
+
147
+ super
148
+ end
149
+
130
150
  protected
131
151
 
132
152
  def base_href
@@ -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(make_params) do |field, params|
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]').each do |option|
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
- events.log 'Capybara starting Puma...'
38
- events.log "* Version #{Puma::Const::PUMA_VERSION} , codename: #{Puma::Const::CODE_NAME}"
39
- events.log "* Min threads: #{conf.options[:min_threads]}, max threads: #{conf.options[:max_threads]}"
40
-
41
- Puma::Server.new(conf.app, events, conf.options).tap do |s|
42
- s.binder.parse conf.options[:binds], s.events
43
- s.min_threads, s.max_threads = conf.options[:min_threads], conf.options[:max_threads]
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 shouble be used if no visibile option is passed when using the selector.
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.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)
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.each_with_object([]) { |s, memo| memo.concat combine(s) }
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
@@ -66,7 +66,11 @@ module Capybara
66
66
  end
67
67
  ensure
68
68
  unless locator_valid?(locator)
69
- warn "Locator #{locator.class}:#{locator.inspect} for selector #{name.inspect} must #{locator_description}. This will raise an error in a future version of Capybara."
69
+ Capybara::Helpers.warn(
70
+ "Locator #{locator.class}:#{locator.inspect} for selector #{name.inspect} must #{locator_description}. " \
71
+ 'This will raise an error in a future version of Capybara. ' \
72
+ "Called from: #{Capybara::Helpers.filter_backtrace(caller)}"
73
+ )
70
74
  end
71
75
  end
72
76
 
@@ -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.each_with_object([]) do |arg, arr|
43
- arg.each_with_object(arr) do |(type, data), arr_|
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
@@ -18,6 +18,10 @@ module Capybara
18
18
  end
19
19
  end
20
20
 
21
+ def warn(*args, **opts)
22
+ super unless @suppress_for_capybara
23
+ end
24
+
21
25
  def suppress_deprecations
22
26
  prev_suppress_for_capybara, @suppress_for_capybara = @suppress_for_capybara, true
23
27
  yield