capybara 3.29.0 → 3.31.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.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +40 -1
  3. data/README.md +1 -1
  4. data/lib/capybara/config.rb +7 -3
  5. data/lib/capybara/dsl.rb +10 -2
  6. data/lib/capybara/helpers.rb +3 -1
  7. data/lib/capybara/minitest.rb +18 -4
  8. data/lib/capybara/node/actions.rb +23 -19
  9. data/lib/capybara/node/document.rb +2 -2
  10. data/lib/capybara/node/document_matchers.rb +3 -3
  11. data/lib/capybara/node/element.rb +21 -16
  12. data/lib/capybara/node/finders.rb +17 -11
  13. data/lib/capybara/node/matchers.rb +60 -45
  14. data/lib/capybara/node/simple.rb +4 -2
  15. data/lib/capybara/queries/ancestor_query.rb +1 -1
  16. data/lib/capybara/queries/base_query.rb +2 -1
  17. data/lib/capybara/queries/selector_query.rb +17 -4
  18. data/lib/capybara/queries/sibling_query.rb +1 -1
  19. data/lib/capybara/rack_test/browser.rb +4 -1
  20. data/lib/capybara/rack_test/driver.rb +1 -1
  21. data/lib/capybara/rack_test/form.rb +1 -1
  22. data/lib/capybara/rack_test/node.rb +34 -9
  23. data/lib/capybara/result.rb +24 -4
  24. data/lib/capybara/rspec/matchers.rb +27 -27
  25. data/lib/capybara/rspec/matchers/base.rb +12 -6
  26. data/lib/capybara/rspec/matchers/count_sugar.rb +2 -1
  27. data/lib/capybara/rspec/matchers/have_ancestor.rb +4 -3
  28. data/lib/capybara/rspec/matchers/have_current_path.rb +2 -2
  29. data/lib/capybara/rspec/matchers/have_selector.rb +15 -7
  30. data/lib/capybara/rspec/matchers/have_sibling.rb +3 -3
  31. data/lib/capybara/rspec/matchers/have_text.rb +2 -2
  32. data/lib/capybara/rspec/matchers/have_title.rb +2 -2
  33. data/lib/capybara/rspec/matchers/match_selector.rb +3 -3
  34. data/lib/capybara/rspec/matchers/match_style.rb +2 -2
  35. data/lib/capybara/rspec/matchers/spatial_sugar.rb +2 -1
  36. data/lib/capybara/selector.rb +24 -16
  37. data/lib/capybara/selector/css.rb +1 -1
  38. data/lib/capybara/selector/definition.rb +2 -2
  39. data/lib/capybara/selector/definition/button.rb +7 -2
  40. data/lib/capybara/selector/definition/checkbox.rb +2 -2
  41. data/lib/capybara/selector/definition/css.rb +3 -1
  42. data/lib/capybara/selector/definition/datalist_input.rb +1 -1
  43. data/lib/capybara/selector/definition/datalist_option.rb +1 -1
  44. data/lib/capybara/selector/definition/element.rb +1 -1
  45. data/lib/capybara/selector/definition/field.rb +1 -1
  46. data/lib/capybara/selector/definition/file_field.rb +1 -1
  47. data/lib/capybara/selector/definition/fillable_field.rb +1 -1
  48. data/lib/capybara/selector/definition/label.rb +4 -2
  49. data/lib/capybara/selector/definition/radio_button.rb +2 -2
  50. data/lib/capybara/selector/definition/select.rb +32 -13
  51. data/lib/capybara/selector/definition/table.rb +5 -2
  52. data/lib/capybara/selector/filter_set.rb +11 -9
  53. data/lib/capybara/selector/filters/base.rb +6 -1
  54. data/lib/capybara/selector/filters/locator_filter.rb +1 -1
  55. data/lib/capybara/selector/selector.rb +4 -2
  56. data/lib/capybara/selenium/driver.rb +19 -11
  57. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +1 -1
  58. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +2 -2
  59. data/lib/capybara/selenium/extensions/html5_drag.rb +30 -13
  60. data/lib/capybara/selenium/node.rb +29 -10
  61. data/lib/capybara/selenium/nodes/chrome_node.rb +11 -5
  62. data/lib/capybara/selenium/nodes/edge_node.rb +4 -2
  63. data/lib/capybara/selenium/nodes/firefox_node.rb +2 -2
  64. data/lib/capybara/server.rb +15 -3
  65. data/lib/capybara/server/checker.rb +1 -1
  66. data/lib/capybara/server/middleware.rb +20 -10
  67. data/lib/capybara/session.rb +40 -23
  68. data/lib/capybara/session/config.rb +6 -2
  69. data/lib/capybara/session/matchers.rb +6 -6
  70. data/lib/capybara/spec/public/test.js +51 -6
  71. data/lib/capybara/spec/session/all_spec.rb +60 -5
  72. data/lib/capybara/spec/session/ancestor_spec.rb +5 -0
  73. data/lib/capybara/spec/session/assert_text_spec.rb +9 -5
  74. data/lib/capybara/spec/session/click_button_spec.rb +5 -0
  75. data/lib/capybara/spec/session/fill_in_spec.rb +20 -0
  76. data/lib/capybara/spec/session/find_spec.rb +20 -0
  77. data/lib/capybara/spec/session/has_css_spec.rb +3 -3
  78. data/lib/capybara/spec/session/has_select_spec.rb +28 -0
  79. data/lib/capybara/spec/session/has_table_spec.rb +51 -5
  80. data/lib/capybara/spec/session/has_text_spec.rb +35 -0
  81. data/lib/capybara/spec/session/node_spec.rb +106 -2
  82. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +2 -2
  83. data/lib/capybara/spec/session/save_screenshot_spec.rb +4 -4
  84. data/lib/capybara/spec/session/selectors_spec.rb +15 -2
  85. data/lib/capybara/spec/views/form.erb +11 -1
  86. data/lib/capybara/version.rb +1 -1
  87. data/spec/dsl_spec.rb +2 -2
  88. data/spec/minitest_spec_spec.rb +46 -46
  89. data/spec/rack_test_spec.rb +0 -1
  90. data/spec/regexp_dissassembler_spec.rb +45 -37
  91. data/spec/result_spec.rb +7 -3
  92. data/spec/rspec/features_spec.rb +1 -0
  93. data/spec/rspec/shared_spec_matchers.rb +3 -3
  94. data/spec/rspec_spec.rb +4 -4
  95. data/spec/selenium_spec_chrome.rb +5 -4
  96. data/spec/selenium_spec_firefox.rb +7 -2
  97. data/spec/server_spec.rb +42 -0
  98. data/spec/session_spec.rb +1 -1
  99. data/spec/shared_selenium_node.rb +3 -3
  100. data/spec/shared_selenium_session.rb +8 -7
  101. metadata +3 -3
@@ -54,7 +54,9 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
54
54
  # :backspace => send backspace keystrokes to clear the field <br/>
55
55
  # Array => an array of keys to send before the value being set, e.g. [[:command, 'a'], :backspace]
56
56
  def set(value, **options)
57
- raise ArgumentError, "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}" if value.is_a?(Array) && !multiple?
57
+ if value.is_a?(Array) && !multiple?
58
+ raise ArgumentError, "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
59
+ end
58
60
 
59
61
  tag_name, type = attrs(:tagName, :type).map { |val| val&.downcase }
60
62
  @tag_name ||= tag_name
@@ -76,11 +78,13 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
76
78
  set_datetime_local(value)
77
79
  when 'color'
78
80
  set_color(value)
81
+ when 'range'
82
+ set_range(value)
79
83
  else
80
- set_text(value, options)
84
+ set_text(value, **options)
81
85
  end
82
86
  when 'textarea'
83
- set_text(value, options)
87
+ set_text(value, **options)
84
88
  else
85
89
  set_content_editable(value)
86
90
  end
@@ -132,11 +136,17 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
132
136
  scroll_if_needed { browser_action.move_to(native).perform }
133
137
  end
134
138
 
135
- def drag_to(element, **)
139
+ def drag_to(element, drop_modifiers: [], **)
140
+ drop_modifiers = Array(drop_modifiers)
136
141
  # Due to W3C spec compliance - The Actions API no longer scrolls to elements when necessary
137
142
  # which means Seleniums `drag_and_drop` is now broken - do it manually
138
143
  scroll_if_needed { browser_action.click_and_hold(native).perform }
139
- element.scroll_if_needed { browser_action.move_to(element.native).release.perform }
144
+ # element.scroll_if_needed { browser_action.move_to(element.native).release.perform }
145
+ element.scroll_if_needed do
146
+ keys_down = modifiers_down(browser_action, drop_modifiers)
147
+ keys_up = modifiers_up(keys_down.move_to(element.native).release, drop_modifiers)
148
+ keys_up.perform
149
+ end
140
150
  end
141
151
 
142
152
  def drop(*_)
@@ -202,7 +212,7 @@ protected
202
212
  JS
203
213
  begin
204
214
  driver.execute_script(script, self)
205
- rescue StandardError # rubocop:disable Lint/HandleExceptions
215
+ rescue StandardError # rubocop:disable Lint/SuppressedException
206
216
  # Swallow error if scrollIntoView with options isn't supported
207
217
  end
208
218
  end
@@ -288,6 +298,10 @@ private
288
298
  update_value_js(value)
289
299
  end
290
300
 
301
+ def set_range(value) # rubocop:disable Naming/AccessorMethodName
302
+ update_value_js(value)
303
+ end
304
+
291
305
  def update_value_js(value)
292
306
  driver.execute_script(<<-JS, self, value)
293
307
  if (arguments[0].readOnly) { return };
@@ -373,10 +387,12 @@ private
373
387
 
374
388
  def modifiers_down(actions, keys)
375
389
  each_key(keys) { |key| actions.key_down(key) }
390
+ actions
376
391
  end
377
392
 
378
393
  def modifiers_up(actions, keys)
379
394
  each_key(keys) { |key| actions.key_up(key) }
395
+ actions
380
396
  end
381
397
 
382
398
  def browser
@@ -391,18 +407,21 @@ private
391
407
  browser.action
392
408
  end
393
409
 
394
- def each_key(keys)
395
- keys.each do |key|
396
- key = case key
410
+ def normalize_keys(keys)
411
+ keys.map do |key|
412
+ case key
397
413
  when :ctrl then :control
398
414
  when :command, :cmd then :meta
399
415
  else
400
416
  key
401
417
  end
402
- yield key
403
418
  end
404
419
  end
405
420
 
421
+ def each_key(keys)
422
+ normalize_keys(keys).each { |key| yield(key) }
423
+ end
424
+
406
425
  def find_context
407
426
  native
408
427
  end
@@ -18,14 +18,16 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
18
18
  # In Chrome 75+ files are appended (due to WebDriver spec - why?) so we have to clear here if its multiple and already set
19
19
  if browser_version >= 75.0
20
20
  driver.execute_script(<<~JS, self)
21
- if (arguments[0].multiple && (arguments[0].files.length > 0)){
21
+ if (arguments[0].multiple && arguments[0].files.length){
22
22
  arguments[0].value = null;
23
23
  }
24
24
  JS
25
25
  end
26
26
  super
27
27
  rescue *file_errors => e
28
- raise ArgumentError, "Selenium < 3.14 with remote Chrome doesn't support multiple file upload" if e.message.match?(/File not found : .+\n.+/m)
28
+ if e.message.match?(/File not found : .+\n.+/m)
29
+ raise ArgumentError, "Selenium < 3.14 with remote Chrome doesn't support multiple file upload"
30
+ end
29
31
 
30
32
  raise
31
33
  end
@@ -34,13 +36,15 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
34
36
  html5_drop(*args)
35
37
  end
36
38
 
37
- def click(*)
39
+ def click(*, **)
38
40
  super
39
41
  rescue ::Selenium::WebDriver::Error::ElementClickInterceptedError
40
42
  raise
41
43
  rescue ::Selenium::WebDriver::Error::WebDriverError => e
42
44
  # chromedriver 74 (at least on mac) raises the wrong error for this
43
- raise ::Selenium::WebDriver::Error::ElementClickInterceptedError, e.message if e.message.match?(/element click intercepted/)
45
+ if e.message.match?(/element click intercepted/)
46
+ raise ::Selenium::WebDriver::Error::ElementClickInterceptedError, e.message
47
+ end
44
48
 
45
49
  raise
46
50
  end
@@ -71,9 +75,11 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
71
75
 
72
76
  private
73
77
 
74
- def perform_legacy_drag(element)
78
+ def perform_legacy_drag(element, drop_modifiers)
75
79
  return super if chromedriver_fixed_actions_key_state? || !w3c? || element.obscured?
76
80
 
81
+ raise ArgumentError, 'Modifier keys are not supported while dragging in this version of Chrome.' unless drop_modifiers.empty?
82
+
77
83
  # W3C Chrome/chromedriver < 77 doesn't maintain mouse button state across actions API performs
78
84
  # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2981
79
85
  browser_action.release.perform
@@ -18,14 +18,16 @@ class Capybara::Selenium::EdgeNode < Capybara::Selenium::Node
18
18
  # In Chrome 75+ files are appended (due to WebDriver spec - why?) so we have to clear here if its multiple and already set
19
19
  if chrome_edge?
20
20
  driver.execute_script(<<~JS, self)
21
- if (arguments[0].multiple && (arguments[0].files.length > 0)){
21
+ if (arguments[0].multiple && arguments[0].files.length){
22
22
  arguments[0].value = null;
23
23
  }
24
24
  JS
25
25
  end
26
26
  super
27
27
  rescue *file_errors => e
28
- raise ArgumentError, "Selenium < 3.14 with remote Chrome doesn't support multiple file upload" if e.message.match?(/File not found : .+\n.+/m)
28
+ if e.message.match?(/File not found : .+\n.+/m)
29
+ raise ArgumentError, "Selenium < 3.14 with remote Chrome doesn't support multiple file upload"
30
+ end
29
31
 
30
32
  raise
31
33
  end
@@ -14,7 +14,7 @@ class Capybara::Selenium::FirefoxNode < Capybara::Selenium::Node
14
14
  warn 'You are attempting to click a table row which has issues in geckodriver/marionette - '\
15
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
- return find_css('th:first-child,td:first-child')[0].click(keys, options)
17
+ return find_css('th:first-child,td:first-child')[0].click(keys, **options)
18
18
  end
19
19
  raise
20
20
  end
@@ -26,7 +26,7 @@ class Capybara::Selenium::FirefoxNode < Capybara::Selenium::Node
26
26
  def set_file(value) # rubocop:disable Naming/AccessorMethodName
27
27
  # By default files are appended so we have to clear here if its multiple and already set
28
28
  driver.execute_script(<<~JS, self)
29
- if (arguments[0].multiple && (arguments[0].files.length > 0)){
29
+ if (arguments[0].multiple && arguments[0].files.length){
30
30
  arguments[0].value = null;
31
31
  }
32
32
  JS
@@ -24,7 +24,9 @@ module Capybara
24
24
  host: Capybara.server_host,
25
25
  reportable_errors: Capybara.server_errors,
26
26
  extra_middleware: [])
27
- warn 'Positional arguments, other than the application, to Server#new are deprecated, please use keyword arguments' unless deprecated_options.empty?
27
+ unless deprecated_options.empty?
28
+ warn 'Positional arguments, other than the application, to Server#new are deprecated, please use keyword arguments'
29
+ end
28
30
  @app = app
29
31
  @extra_middleware = extra_middleware
30
32
  @server_thread = nil # suppress warnings
@@ -61,7 +63,7 @@ module Capybara
61
63
  def wait_for_pending_requests
62
64
  timer = Capybara::Helpers.timer(expire_in: 60)
63
65
  while pending_requests?
64
- raise 'Requests did not finish in 60 seconds' if timer.expired?
66
+ raise "Requests did not finish in 60 seconds: #{middleware.pending_requests}" if timer.expired?
65
67
 
66
68
  sleep 0.01
67
69
  end
@@ -106,7 +108,17 @@ module Capybara
106
108
 
107
109
  def find_available_port(host)
108
110
  server = TCPServer.new(host, 0)
109
- server.addr[1]
111
+ port = server.addr[1]
112
+ server.close
113
+
114
+ # Workaround issue where some platforms (mac, ???) when passed a host
115
+ # of '0.0.0.0' will return a port that is only available on one of the
116
+ # ip addresses that resolves to, but the next binding to that port requires
117
+ # that port to be available on all ips
118
+ server = TCPServer.new(host, port)
119
+ port
120
+ rescue Errno::EADDRINUSE
121
+ retry
110
122
  ensure
111
123
  server&.close
112
124
  end
@@ -29,7 +29,7 @@ module Capybara
29
29
  end
30
30
 
31
31
  def https_request(&block)
32
- make_request(ssl_options, &block)
32
+ make_request(**ssl_options, &block)
33
33
  end
34
34
 
35
35
  def make_request(**options, &block)
@@ -4,19 +4,25 @@ module Capybara
4
4
  class Server
5
5
  class Middleware
6
6
  class Counter
7
- attr_reader :value
8
-
9
7
  def initialize
10
- @value = 0
8
+ @value = []
11
9
  @mutex = Mutex.new
12
10
  end
13
11
 
14
- def increment
15
- @mutex.synchronize { @value += 1 }
12
+ def increment(uri)
13
+ @mutex.synchronize { @value.push(uri) }
14
+ end
15
+
16
+ def decrement(uri)
17
+ @mutex.synchronize { @value.delete_at(@value.index(uri) || @value.length) }
16
18
  end
17
19
 
18
- def decrement
19
- @mutex.synchronize { @value -= 1 }
20
+ def positive?
21
+ @mutex.synchronize { @value.length.positive? }
22
+ end
23
+
24
+ def value
25
+ @mutex.synchronize { @value.dup }
20
26
  end
21
27
  end
22
28
 
@@ -31,8 +37,12 @@ module Capybara
31
37
  @server_errors = server_errors
32
38
  end
33
39
 
40
+ def pending_requests
41
+ @counter.value
42
+ end
43
+
34
44
  def pending_requests?
35
- @counter.value.positive?
45
+ @counter.positive?
36
46
  end
37
47
 
38
48
  def clear_error
@@ -43,14 +53,14 @@ module Capybara
43
53
  if env['PATH_INFO'] == '/__identify__'
44
54
  [200, {}, [@app.object_id.to_s]]
45
55
  else
46
- @counter.increment
56
+ @counter.increment(env['REQUEST_URI'])
47
57
  begin
48
58
  @extended_app.call(env)
49
59
  rescue *@server_errors => e
50
60
  @error ||= e
51
61
  raise e
52
62
  ensure
53
- @counter.decrement
63
+ @counter.decrement(env['REQUEST_URI'])
54
64
  end
55
65
  end
56
66
  end
@@ -75,7 +75,9 @@ module Capybara
75
75
  attr_accessor :synchronized
76
76
 
77
77
  def initialize(mode, app = nil)
78
- raise TypeError, 'The second parameter to Session::new should be a rack app if passed.' if app && !app.respond_to?(:call)
78
+ if app && !app.respond_to?(:call)
79
+ raise TypeError, 'The second parameter to Session::new should be a rack app if passed.'
80
+ end
79
81
 
80
82
  @@instance_created = true # rubocop:disable Style/ClassVars
81
83
  @mode = mode
@@ -88,7 +90,7 @@ module Capybara
88
90
  @server = if config.run_server && @app && driver.needs_server?
89
91
  server_options = { port: config.server_port, host: config.server_host, reportable_errors: config.server_errors }
90
92
  server_options[:extra_middleware] = [Capybara::Server::AnimationDisabler] if config.disable_animation
91
- Capybara::Server.new(@app, server_options).boot
93
+ Capybara::Server.new(@app, **server_options).boot
92
94
  end
93
95
  @touched = false
94
96
  end
@@ -336,8 +338,8 @@ module Capybara
336
338
  #
337
339
  # @raise [Capybara::ElementNotFound] If the scope can't be found before time expires
338
340
  #
339
- def within(*args)
340
- new_scope = args.first.respond_to?(:to_capybara_node) ? args.first.to_capybara_node : find(*args)
341
+ def within(*args, **kw_args)
342
+ new_scope = args.first.respond_to?(:to_capybara_node) ? args.first.to_capybara_node : find(*args, **kw_args)
341
343
  begin
342
344
  scopes.push(new_scope)
343
345
  yield if block_given?
@@ -421,8 +423,8 @@ module Capybara
421
423
  # @param [String] locator The locator for the given selector kind. For :frame this is the name/id of a frame/iframe element
422
424
  # @overload within_frame(index)
423
425
  # @param [Integer] index index of a frame (0 based)
424
- def within_frame(*args)
425
- switch_to_frame(_find_frame(*args))
426
+ def within_frame(*args, **kw_args)
427
+ switch_to_frame(_find_frame(*args, **kw_args))
426
428
  begin
427
429
  yield if block_given?
428
430
  ensure
@@ -494,7 +496,7 @@ module Capybara
494
496
  '`within` or `within_frame` blocks.'
495
497
  end
496
498
 
497
- _switch_to_window(window, options, &window_locator)
499
+ _switch_to_window(window, **options, &window_locator)
498
500
  end
499
501
 
500
502
  ##
@@ -528,7 +530,7 @@ module Capybara
528
530
  when Proc
529
531
  _switch_to_window { window_or_proc.call }
530
532
  else
531
- raise ArgumentError('`#within_window` requires a `Capybara::Window` instance or a lambda')
533
+ raise ArgumentError, '`#within_window` requires a `Capybara::Window` instance or a lambda'
532
534
  end
533
535
 
534
536
  begin
@@ -721,7 +723,7 @@ module Capybara
721
723
  # @param [Hash] options a customizable set of options
722
724
  # @return [String] the path to which the file was saved
723
725
  def save_screenshot(path = nil, **options)
724
- prepare_path(path, 'png').tap { |p_path| driver.save_screenshot(p_path, options) }
726
+ prepare_path(path, 'png').tap { |p_path| driver.save_screenshot(p_path, **options) }
725
727
  end
726
728
 
727
729
  ##
@@ -736,7 +738,7 @@ module Capybara
736
738
  # @param [Hash] options a customizable set of options
737
739
  #
738
740
  def save_and_open_screenshot(path = nil, **options)
739
- save_screenshot(path, options).tap { |s_path| open_file(s_path) } # rubocop:disable Lint/Debugger
741
+ save_screenshot(path, **options).tap { |s_path| open_file(s_path) } # rubocop:disable Lint/Debugger
740
742
  end
741
743
 
742
744
  def document
@@ -744,15 +746,32 @@ module Capybara
744
746
  end
745
747
 
746
748
  NODE_METHODS.each do |method|
747
- define_method method do |*args, &block|
748
- @touched = true
749
- current_scope.send(method, *args, &block)
749
+ if RUBY_VERSION >= '2.7'
750
+ class_eval <<~METHOD, __FILE__, __LINE__ + 1
751
+ def #{method}(...)
752
+ @touched = true
753
+ current_scope.#{method}(...)
754
+ end
755
+ METHOD
756
+ else
757
+ define_method method do |*args, &block|
758
+ @touched = true
759
+ current_scope.send(method, *args, &block)
760
+ end
750
761
  end
751
762
  end
752
763
 
753
764
  DOCUMENT_METHODS.each do |method|
754
- define_method method do |*args, &block|
755
- document.send(method, *args, &block)
765
+ if RUBY_VERSION >= '2.7'
766
+ class_eval <<~METHOD, __FILE__, __LINE__ + 1
767
+ def #{method}(...)
768
+ document.#{method}(...)
769
+ end
770
+ METHOD
771
+ else
772
+ define_method method do |*args, &block|
773
+ document.send(method, *args, &block)
774
+ end
756
775
  end
757
776
  end
758
777
 
@@ -819,11 +838,11 @@ module Capybara
819
838
  end
820
839
 
821
840
  def accept_modal(type, text_or_options, options, &blk)
822
- driver.accept_modal(type, modal_options(text_or_options, options), &blk)
841
+ driver.accept_modal(type, **modal_options(text_or_options, **options), &blk)
823
842
  end
824
843
 
825
844
  def dismiss_modal(type, text_or_options, options, &blk)
826
- driver.dismiss_modal(type, modal_options(text_or_options, options), &blk)
845
+ driver.dismiss_modal(type, **modal_options(text_or_options, **options), &blk)
827
846
  end
828
847
 
829
848
  def modal_options(text = nil, **options)
@@ -871,16 +890,14 @@ module Capybara
871
890
  uri.port ||= @server.port if @server && config.always_include_port
872
891
  end
873
892
 
874
- def _find_frame(*args)
875
- return find(:frame) if args.length.zero?
876
-
893
+ def _find_frame(*args, **kw_args)
877
894
  case args[0]
878
895
  when Capybara::Node::Element
879
896
  args[0]
880
- when String, Hash
881
- find(:frame, *args)
897
+ when String, nil
898
+ find(:frame, *args, **kw_args)
882
899
  when Symbol
883
- find(*args)
900
+ find(*args, **kw_args)
884
901
  when Integer
885
902
  idx = args[0]
886
903
  all(:frame, minimum: idx + 1)[idx]
@@ -79,14 +79,18 @@ module Capybara
79
79
 
80
80
  remove_method :app_host=
81
81
  def app_host=(url)
82
- raise ArgumentError, "Capybara.app_host should be set to a url (http://www.example.com). Attempted to set #{url.inspect}." unless url.nil? || url.match?(URI::DEFAULT_PARSER.make_regexp)
82
+ unless url.nil? || url.match?(URI::DEFAULT_PARSER.make_regexp)
83
+ raise ArgumentError, "Capybara.app_host should be set to a url (http://www.example.com). Attempted to set #{url.inspect}."
84
+ end
83
85
 
84
86
  @app_host = url
85
87
  end
86
88
 
87
89
  remove_method :default_host=
88
90
  def default_host=(url)
89
- raise ArgumentError, "Capybara.default_host should be set to a url (http://www.example.com). Attempted to set #{url.inspect}." unless url.nil? || url.match?(URI::DEFAULT_PARSER.make_regexp)
91
+ unless url.nil? || url.match?(URI::DEFAULT_PARSER.make_regexp)
92
+ raise ArgumentError, "Capybara.default_host should be set to a url (http://www.example.com). Attempted to set #{url.inspect}."
93
+ end
90
94
 
91
95
  @default_host = url
92
96
  end