capybara 3.13.2 → 3.40.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 (260) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/History.md +587 -16
  4. data/README.md +240 -90
  5. data/lib/capybara/config.rb +24 -11
  6. data/lib/capybara/cucumber.rb +1 -1
  7. data/lib/capybara/driver/base.rb +8 -0
  8. data/lib/capybara/driver/node.rb +20 -4
  9. data/lib/capybara/dsl.rb +5 -3
  10. data/lib/capybara/helpers.rb +25 -4
  11. data/lib/capybara/minitest/spec.rb +174 -90
  12. data/lib/capybara/minitest.rb +256 -142
  13. data/lib/capybara/node/actions.rb +123 -77
  14. data/lib/capybara/node/base.rb +20 -12
  15. data/lib/capybara/node/document.rb +2 -2
  16. data/lib/capybara/node/document_matchers.rb +3 -3
  17. data/lib/capybara/node/element.rb +223 -117
  18. data/lib/capybara/node/finders.rb +81 -71
  19. data/lib/capybara/node/matchers.rb +271 -134
  20. data/lib/capybara/node/simple.rb +18 -5
  21. data/lib/capybara/node/whitespace_normalizer.rb +81 -0
  22. data/lib/capybara/queries/active_element_query.rb +18 -0
  23. data/lib/capybara/queries/ancestor_query.rb +8 -9
  24. data/lib/capybara/queries/base_query.rb +3 -2
  25. data/lib/capybara/queries/current_path_query.rb +15 -5
  26. data/lib/capybara/queries/selector_query.rb +364 -54
  27. data/lib/capybara/queries/sibling_query.rb +8 -6
  28. data/lib/capybara/queries/style_query.rb +2 -2
  29. data/lib/capybara/queries/text_query.rb +13 -1
  30. data/lib/capybara/queries/title_query.rb +1 -1
  31. data/lib/capybara/rack_test/browser.rb +76 -11
  32. data/lib/capybara/rack_test/driver.rb +10 -5
  33. data/lib/capybara/rack_test/errors.rb +6 -0
  34. data/lib/capybara/rack_test/form.rb +31 -9
  35. data/lib/capybara/rack_test/node.rb +74 -23
  36. data/lib/capybara/registration_container.rb +41 -0
  37. data/lib/capybara/registrations/drivers.rb +42 -0
  38. data/lib/capybara/registrations/patches/puma_ssl.rb +29 -0
  39. data/lib/capybara/registrations/servers.rb +66 -0
  40. data/lib/capybara/result.rb +44 -20
  41. data/lib/capybara/rspec/matcher_proxies.rb +13 -11
  42. data/lib/capybara/rspec/matchers/base.rb +31 -16
  43. data/lib/capybara/rspec/matchers/compound.rb +1 -1
  44. data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
  45. data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
  46. data/lib/capybara/rspec/matchers/have_current_path.rb +2 -2
  47. data/lib/capybara/rspec/matchers/have_selector.rb +21 -21
  48. data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
  49. data/lib/capybara/rspec/matchers/have_text.rb +4 -4
  50. data/lib/capybara/rspec/matchers/have_title.rb +2 -2
  51. data/lib/capybara/rspec/matchers/match_selector.rb +3 -3
  52. data/lib/capybara/rspec/matchers/match_style.rb +7 -2
  53. data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
  54. data/lib/capybara/rspec/matchers.rb +111 -68
  55. data/lib/capybara/rspec.rb +2 -0
  56. data/lib/capybara/selector/builders/css_builder.rb +11 -7
  57. data/lib/capybara/selector/builders/xpath_builder.rb +5 -3
  58. data/lib/capybara/selector/css.rb +11 -9
  59. data/lib/capybara/selector/definition/button.rb +68 -0
  60. data/lib/capybara/selector/definition/checkbox.rb +26 -0
  61. data/lib/capybara/selector/definition/css.rb +10 -0
  62. data/lib/capybara/selector/definition/datalist_input.rb +35 -0
  63. data/lib/capybara/selector/definition/datalist_option.rb +25 -0
  64. data/lib/capybara/selector/definition/element.rb +28 -0
  65. data/lib/capybara/selector/definition/field.rb +40 -0
  66. data/lib/capybara/selector/definition/fieldset.rb +14 -0
  67. data/lib/capybara/selector/definition/file_field.rb +13 -0
  68. data/lib/capybara/selector/definition/fillable_field.rb +33 -0
  69. data/lib/capybara/selector/definition/frame.rb +17 -0
  70. data/lib/capybara/selector/definition/id.rb +6 -0
  71. data/lib/capybara/selector/definition/label.rb +62 -0
  72. data/lib/capybara/selector/definition/link.rb +55 -0
  73. data/lib/capybara/selector/definition/link_or_button.rb +16 -0
  74. data/lib/capybara/selector/definition/option.rb +27 -0
  75. data/lib/capybara/selector/definition/radio_button.rb +27 -0
  76. data/lib/capybara/selector/definition/select.rb +81 -0
  77. data/lib/capybara/selector/definition/table.rb +109 -0
  78. data/lib/capybara/selector/definition/table_row.rb +21 -0
  79. data/lib/capybara/selector/definition/xpath.rb +5 -0
  80. data/lib/capybara/selector/definition.rb +280 -0
  81. data/lib/capybara/selector/filter_set.rb +19 -18
  82. data/lib/capybara/selector/filters/base.rb +11 -2
  83. data/lib/capybara/selector/filters/locator_filter.rb +13 -3
  84. data/lib/capybara/selector/regexp_disassembler.rb +11 -7
  85. data/lib/capybara/selector/selector.rb +50 -440
  86. data/lib/capybara/selector/xpath_extensions.rb +17 -0
  87. data/lib/capybara/selector.rb +473 -482
  88. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -0
  89. data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -0
  90. data/lib/capybara/selenium/atoms/src/getAttribute.js +161 -0
  91. data/lib/capybara/selenium/atoms/src/isDisplayed.js +454 -0
  92. data/lib/capybara/selenium/driver.rb +174 -62
  93. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +74 -18
  94. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +128 -0
  95. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +37 -3
  96. data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +14 -1
  97. data/lib/capybara/selenium/driver_specializations/safari_driver.rb +24 -0
  98. data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
  99. data/lib/capybara/selenium/extensions/find.rb +68 -45
  100. data/lib/capybara/selenium/extensions/html5_drag.rb +192 -22
  101. data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
  102. data/lib/capybara/selenium/extensions/scroll.rb +8 -10
  103. data/lib/capybara/selenium/node.rb +268 -72
  104. data/lib/capybara/selenium/nodes/chrome_node.rb +105 -9
  105. data/lib/capybara/selenium/nodes/edge_node.rb +110 -0
  106. data/lib/capybara/selenium/nodes/firefox_node.rb +51 -61
  107. data/lib/capybara/selenium/nodes/ie_node.rb +22 -0
  108. data/lib/capybara/selenium/nodes/safari_node.rb +118 -0
  109. data/lib/capybara/selenium/patches/atoms.rb +18 -0
  110. data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
  111. data/lib/capybara/selenium/patches/logs.rb +45 -0
  112. data/lib/capybara/selenium/patches/pause_duration_fix.rb +1 -1
  113. data/lib/capybara/selenium/patches/persistent_client.rb +20 -0
  114. data/lib/capybara/server/animation_disabler.rb +43 -21
  115. data/lib/capybara/server/checker.rb +6 -2
  116. data/lib/capybara/server/middleware.rb +25 -13
  117. data/lib/capybara/server.rb +20 -4
  118. data/lib/capybara/session/config.rb +15 -11
  119. data/lib/capybara/session/matchers.rb +11 -11
  120. data/lib/capybara/session.rb +162 -131
  121. data/lib/capybara/spec/public/offset.js +6 -0
  122. data/lib/capybara/spec/public/test.js +105 -6
  123. data/lib/capybara/spec/session/accept_alert_spec.rb +1 -1
  124. data/lib/capybara/spec/session/active_element_spec.rb +31 -0
  125. data/lib/capybara/spec/session/all_spec.rb +89 -15
  126. data/lib/capybara/spec/session/ancestor_spec.rb +5 -0
  127. data/lib/capybara/spec/session/assert_current_path_spec.rb +5 -2
  128. data/lib/capybara/spec/session/assert_text_spec.rb +26 -22
  129. data/lib/capybara/spec/session/attach_file_spec.rb +64 -31
  130. data/lib/capybara/spec/session/check_spec.rb +26 -4
  131. data/lib/capybara/spec/session/choose_spec.rb +14 -2
  132. data/lib/capybara/spec/session/click_button_spec.rb +109 -61
  133. data/lib/capybara/spec/session/click_link_or_button_spec.rb +9 -0
  134. data/lib/capybara/spec/session/click_link_spec.rb +23 -1
  135. data/lib/capybara/spec/session/current_scope_spec.rb +1 -1
  136. data/lib/capybara/spec/session/current_url_spec.rb +11 -1
  137. data/lib/capybara/spec/session/element/matches_selector_spec.rb +40 -39
  138. data/lib/capybara/spec/session/evaluate_script_spec.rb +12 -0
  139. data/lib/capybara/spec/session/fill_in_spec.rb +46 -5
  140. data/lib/capybara/spec/session/find_link_spec.rb +10 -0
  141. data/lib/capybara/spec/session/find_spec.rb +80 -7
  142. data/lib/capybara/spec/session/first_spec.rb +2 -2
  143. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +14 -1
  144. data/lib/capybara/spec/session/frame/within_frame_spec.rb +14 -1
  145. data/lib/capybara/spec/session/has_all_selectors_spec.rb +5 -5
  146. data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
  147. data/lib/capybara/spec/session/has_any_selectors_spec.rb +6 -2
  148. data/lib/capybara/spec/session/has_button_spec.rb +81 -0
  149. data/lib/capybara/spec/session/has_css_spec.rb +45 -8
  150. data/lib/capybara/spec/session/has_current_path_spec.rb +22 -7
  151. data/lib/capybara/spec/session/has_element_spec.rb +47 -0
  152. data/lib/capybara/spec/session/has_field_spec.rb +59 -1
  153. data/lib/capybara/spec/session/has_link_spec.rb +40 -0
  154. data/lib/capybara/spec/session/has_none_selectors_spec.rb +7 -7
  155. data/lib/capybara/spec/session/has_select_spec.rb +42 -8
  156. data/lib/capybara/spec/session/has_selector_spec.rb +19 -4
  157. data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
  158. data/lib/capybara/spec/session/has_table_spec.rb +177 -0
  159. data/lib/capybara/spec/session/has_text_spec.rb +31 -3
  160. data/lib/capybara/spec/session/html_spec.rb +1 -1
  161. data/lib/capybara/spec/session/matches_style_spec.rb +6 -4
  162. data/lib/capybara/spec/session/node_spec.rb +697 -23
  163. data/lib/capybara/spec/session/node_wrapper_spec.rb +1 -1
  164. data/lib/capybara/spec/session/refresh_spec.rb +2 -1
  165. data/lib/capybara/spec/session/reset_session_spec.rb +21 -7
  166. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +2 -2
  167. data/lib/capybara/spec/session/save_page_spec.rb +4 -4
  168. data/lib/capybara/spec/session/save_screenshot_spec.rb +4 -4
  169. data/lib/capybara/spec/session/scroll_spec.rb +9 -7
  170. data/lib/capybara/spec/session/select_spec.rb +5 -10
  171. data/lib/capybara/spec/session/selectors_spec.rb +24 -3
  172. data/lib/capybara/spec/session/uncheck_spec.rb +3 -3
  173. data/lib/capybara/spec/session/unselect_spec.rb +1 -1
  174. data/lib/capybara/spec/session/visit_spec.rb +20 -0
  175. data/lib/capybara/spec/session/window/become_closed_spec.rb +20 -17
  176. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +1 -1
  177. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +1 -1
  178. data/lib/capybara/spec/session/window/window_spec.rb +54 -57
  179. data/lib/capybara/spec/session/window/windows_spec.rb +2 -2
  180. data/lib/capybara/spec/session/within_spec.rb +36 -0
  181. data/lib/capybara/spec/spec_helper.rb +30 -19
  182. data/lib/capybara/spec/test_app.rb +122 -34
  183. data/lib/capybara/spec/views/animated.erb +49 -0
  184. data/lib/capybara/spec/views/form.erb +86 -8
  185. data/lib/capybara/spec/views/frame_child.erb +3 -2
  186. data/lib/capybara/spec/views/frame_one.erb +2 -1
  187. data/lib/capybara/spec/views/frame_parent.erb +1 -1
  188. data/lib/capybara/spec/views/frame_two.erb +1 -1
  189. data/lib/capybara/spec/views/initial_alert.erb +2 -1
  190. data/lib/capybara/spec/views/layout.erb +10 -0
  191. data/lib/capybara/spec/views/obscured.erb +10 -10
  192. data/lib/capybara/spec/views/offset.erb +33 -0
  193. data/lib/capybara/spec/views/path.erb +2 -2
  194. data/lib/capybara/spec/views/popup_one.erb +1 -1
  195. data/lib/capybara/spec/views/popup_two.erb +1 -1
  196. data/lib/capybara/spec/views/react.erb +45 -0
  197. data/lib/capybara/spec/views/scroll.erb +2 -1
  198. data/lib/capybara/spec/views/spatial.erb +31 -0
  199. data/lib/capybara/spec/views/tables.erb +67 -0
  200. data/lib/capybara/spec/views/with_animation.erb +39 -4
  201. data/lib/capybara/spec/views/with_base_tag.erb +2 -2
  202. data/lib/capybara/spec/views/with_dragula.erb +24 -0
  203. data/lib/capybara/spec/views/with_fixed_header_footer.erb +2 -1
  204. data/lib/capybara/spec/views/with_hover.erb +3 -2
  205. data/lib/capybara/spec/views/with_hover1.erb +10 -0
  206. data/lib/capybara/spec/views/with_html.erb +34 -6
  207. data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
  208. data/lib/capybara/spec/views/with_js.erb +7 -4
  209. data/lib/capybara/spec/views/with_jstree.erb +26 -0
  210. data/lib/capybara/spec/views/with_namespace.erb +1 -0
  211. data/lib/capybara/spec/views/with_scope.erb +2 -2
  212. data/lib/capybara/spec/views/with_scope_other.erb +6 -0
  213. data/lib/capybara/spec/views/with_shadow.erb +31 -0
  214. data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
  215. data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
  216. data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
  217. data/lib/capybara/spec/views/with_windows.erb +1 -1
  218. data/lib/capybara/spec/views/within_frames.erb +1 -1
  219. data/lib/capybara/version.rb +1 -1
  220. data/lib/capybara/window.rb +14 -18
  221. data/lib/capybara.rb +91 -126
  222. data/spec/basic_node_spec.rb +30 -16
  223. data/spec/capybara_spec.rb +40 -28
  224. data/spec/counter_spec.rb +35 -0
  225. data/spec/css_builder_spec.rb +3 -1
  226. data/spec/css_splitter_spec.rb +1 -1
  227. data/spec/dsl_spec.rb +33 -22
  228. data/spec/filter_set_spec.rb +5 -5
  229. data/spec/fixtures/selenium_driver_rspec_failure.rb +3 -3
  230. data/spec/fixtures/selenium_driver_rspec_success.rb +3 -3
  231. data/spec/minitest_spec.rb +24 -2
  232. data/spec/minitest_spec_spec.rb +60 -45
  233. data/spec/per_session_config_spec.rb +1 -1
  234. data/spec/rack_test_spec.rb +131 -98
  235. data/spec/regexp_dissassembler_spec.rb +53 -39
  236. data/spec/result_spec.rb +68 -66
  237. data/spec/rspec/features_spec.rb +9 -4
  238. data/spec/rspec/scenarios_spec.rb +6 -2
  239. data/spec/rspec/shared_spec_matchers.rb +137 -98
  240. data/spec/rspec_matchers_spec.rb +25 -0
  241. data/spec/rspec_spec.rb +23 -21
  242. data/spec/sauce_spec_chrome.rb +43 -0
  243. data/spec/selector_spec.rb +77 -21
  244. data/spec/selenium_spec_chrome.rb +141 -39
  245. data/spec/selenium_spec_chrome_remote.rb +32 -17
  246. data/spec/selenium_spec_edge.rb +36 -8
  247. data/spec/selenium_spec_firefox.rb +110 -68
  248. data/spec/selenium_spec_firefox_remote.rb +22 -15
  249. data/spec/selenium_spec_ie.rb +29 -22
  250. data/spec/selenium_spec_safari.rb +162 -0
  251. data/spec/server_spec.rb +153 -81
  252. data/spec/session_spec.rb +11 -4
  253. data/spec/shared_selenium_node.rb +79 -0
  254. data/spec/shared_selenium_session.rb +179 -74
  255. data/spec/spec_helper.rb +80 -5
  256. data/spec/whitespace_normalizer_spec.rb +54 -0
  257. data/spec/xpath_builder_spec.rb +3 -1
  258. metadata +218 -30
  259. data/lib/capybara/spec/session/source_spec.rb +0 -0
  260. data/lib/capybara/spec/views/with_title.erb +0 -5
@@ -6,7 +6,7 @@ require 'addressable/uri'
6
6
  module Capybara
7
7
  ##
8
8
  #
9
- # The Session class represents a single user's interaction with the system. The Session can use
9
+ # The {Session} class represents a single user's interaction with the system. The {Session} can use
10
10
  # any of the underlying drivers. A session can be initialized manually like this:
11
11
  #
12
12
  # session = Capybara::Session.new(:culerity, MyRackApp)
@@ -17,33 +17,35 @@ module Capybara
17
17
  # session = Capybara::Session.new(:culerity)
18
18
  # session.visit('http://www.google.com')
19
19
  #
20
- # When Capybara.threadsafe == true the sessions options will be initially set to the
20
+ # When {Capybara.configure threadsafe} is `true` the sessions options will be initially set to the
21
21
  # current values of the global options and a configuration block can be passed to the session initializer.
22
- # For available options see {Capybara::SessionConfig::OPTIONS}
22
+ # For available options see {Capybara::SessionConfig::OPTIONS}:
23
23
  #
24
24
  # session = Capybara::Session.new(:driver, MyRackApp) do |config|
25
25
  # config.app_host = "http://my_host.dev"
26
26
  # end
27
27
  #
28
- # Session provides a number of methods for controlling the navigation of the page, such as +visit+,
29
- # +current_path, and so on. It also delegates a number of methods to a Capybara::Document, representing
28
+ # The {Session} provides a number of methods for controlling the navigation of the page, such as {#visit},
29
+ # {#current_path}, and so on. It also delegates a number of methods to a {Capybara::Document}, representing
30
30
  # the current HTML document. This allows interaction:
31
31
  #
32
32
  # session.fill_in('q', with: 'Capybara')
33
33
  # session.click_button('Search')
34
34
  # expect(session).to have_content('Capybara')
35
35
  #
36
- # When using capybara/dsl, the Session is initialized automatically for you.
36
+ # When using `capybara/dsl`, the {Session} is initialized automatically for you.
37
37
  #
38
38
  class Session
39
39
  include Capybara::SessionMatchers
40
40
 
41
41
  NODE_METHODS = %i[
42
42
  all first attach_file text check choose scroll_to scroll_by
43
+ click double_click right_click
43
44
  click_link_or_button click_button click_link
44
45
  fill_in find find_all find_button find_by_id find_field find_link
45
46
  has_content? has_text? has_css? has_no_content? has_no_text?
46
47
  has_no_css? has_no_xpath? has_xpath? select uncheck
48
+ has_element? has_no_element?
47
49
  has_link? has_no_link? has_button? has_no_button? has_field?
48
50
  has_no_field? has_checked_field? has_unchecked_field?
49
51
  has_no_table? has_table? unselect has_select? has_no_select?
@@ -58,7 +60,7 @@ module Capybara
58
60
  ].freeze
59
61
  SESSION_METHODS = %i[
60
62
  body html source current_url current_host current_path
61
- execute_script evaluate_script visit refresh go_back go_forward
63
+ execute_script evaluate_script evaluate_async_script visit refresh go_back go_forward send_keys
62
64
  within within_element within_fieldset within_table within_frame switch_to_frame
63
65
  current_window windows open_new_window switch_to_window within_window window_opened_by
64
66
  save_page save_and_open_page save_screenshot
@@ -75,9 +77,11 @@ module Capybara
75
77
  attr_accessor :synchronized
76
78
 
77
79
  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)
80
+ if app && !app.respond_to?(:call)
81
+ raise TypeError, 'The second parameter to Session::new should be a rack app if passed.'
82
+ end
79
83
 
80
- @@instance_created = true
84
+ @@instance_created = true # rubocop:disable Style/ClassVars
81
85
  @mode = mode
82
86
  @app = app
83
87
  if block_given?
@@ -88,15 +92,15 @@ module Capybara
88
92
  @server = if config.run_server && @app && driver.needs_server?
89
93
  server_options = { port: config.server_port, host: config.server_host, reportable_errors: config.server_errors }
90
94
  server_options[:extra_middleware] = [Capybara::Server::AnimationDisabler] if config.disable_animation
91
- Capybara::Server.new(@app, server_options).boot
95
+ Capybara::Server.new(@app, **server_options).boot
92
96
  end
93
97
  @touched = false
94
98
  end
95
99
 
96
100
  def driver
97
101
  @driver ||= begin
98
- unless Capybara.drivers.key?(mode)
99
- other_drivers = Capybara.drivers.keys.map(&:inspect)
102
+ unless Capybara.drivers[mode]
103
+ other_drivers = Capybara.drivers.names.map(&:inspect)
100
104
  raise Capybara::DriverNotFoundError, "no driver called #{mode.inspect} was found, available drivers: #{other_drivers.join(', ')}"
101
105
  end
102
106
  driver = Capybara.drivers[mode].call(app)
@@ -107,26 +111,28 @@ module Capybara
107
111
 
108
112
  ##
109
113
  #
110
- # Reset the session (i.e. remove cookies and navigate to blank page)
114
+ # Reset the session (i.e. remove cookies and navigate to blank page).
111
115
  #
112
116
  # This method does not:
113
117
  #
114
- # * accept modal dialogs if they are present (Selenium driver now does, others may not)
115
- # * clear browser cache/HTML 5 local storage/IndexedDB/Web SQL database/etc.
116
- # * modify state of the driver/underlying browser in any other way
118
+ # * accept modal dialogs if they are present (Selenium driver now does, others may not)
119
+ # * clear browser cache/HTML 5 local storage/IndexedDB/Web SQL database/etc.
120
+ # * modify state of the driver/underlying browser in any other way
117
121
  #
118
122
  # as doing so will result in performance downsides and it's not needed to do everything from the list above for most apps.
119
123
  #
120
124
  # If you want to do anything from the list above on a general basis you can:
121
125
  #
122
- # * write RSpec/Cucumber/etc. after hook
123
- # * monkeypatch this method
124
- # * use Ruby's `prepend` method
126
+ # * write RSpec/Cucumber/etc. after hook
127
+ # * monkeypatch this method
128
+ # * use Ruby's `prepend` method
125
129
  #
126
130
  def reset!
127
131
  if @touched
128
132
  driver.reset!
129
133
  @touched = false
134
+ switch_to_frame(:top) rescue nil # rubocop:disable Style/RescueModifier
135
+ @scopes = [nil]
130
136
  end
131
137
  @server&.wait_for_pending_requests
132
138
  raise_server_error!
@@ -136,19 +142,18 @@ module Capybara
136
142
 
137
143
  ##
138
144
  #
139
- # Disconnect from the current driver. A new driver will be instantiated on the next interaction
140
- #
145
+ # Disconnect from the current driver. A new driver will be instantiated on the next interaction.
141
146
  #
142
147
  def quit
143
148
  @driver.quit if @driver.respond_to? :quit
144
- @driver = nil
149
+ @document = @driver = nil
145
150
  @touched = false
146
151
  @server&.reset_error!
147
152
  end
148
153
 
149
154
  ##
150
155
  #
151
- # Raise errors encountered in the server
156
+ # Raise errors encountered in the server.
152
157
  #
153
158
  def raise_server_error!
154
159
  return unless @server&.error
@@ -158,9 +163,8 @@ module Capybara
158
163
  if config.raise_server_errors
159
164
  raise CapybaraError, 'Your application server raised an error - It has been raised in your test code because Capybara.raise_server_errors == true'
160
165
  end
161
- rescue CapybaraError
162
- # needed to get the cause set correctly in JRuby -- otherwise we could just do raise @server.error
163
- raise @server.error, @server.error.message, @server.error.backtrace
166
+ rescue CapybaraError => capy_error # rubocop:disable Naming/RescuedExceptionsVariableName
167
+ raise @server.error, cause: capy_error
164
168
  ensure
165
169
  @server.reset_error!
166
170
  end
@@ -168,9 +172,9 @@ module Capybara
168
172
 
169
173
  ##
170
174
  #
171
- # Returns a hash of response headers. Not supported by all drivers (e.g. Selenium)
175
+ # Returns a hash of response headers. Not supported by all drivers (e.g. Selenium).
172
176
  #
173
- # @return [Hash{String => String}] A hash of response headers.
177
+ # @return [Hash<String, String>] A hash of response headers.
174
178
  #
175
179
  def response_headers
176
180
  driver.response_headers
@@ -178,7 +182,7 @@ module Capybara
178
182
 
179
183
  ##
180
184
  #
181
- # Returns the current HTTP status code as an Integer. Not supported by all drivers (e.g. Selenium)
185
+ # Returns the current HTTP status code as an integer. Not supported by all drivers (e.g. Selenium).
182
186
  #
183
187
  # @return [Integer] Current HTTP status code
184
188
  #
@@ -191,7 +195,7 @@ module Capybara
191
195
  # @return [String] A snapshot of the DOM of the current document, as it looks right now (potentially modified by JavaScript).
192
196
  #
193
197
  def html
194
- driver.html
198
+ driver.html || ''
195
199
  end
196
200
  alias_method :body, :html
197
201
  alias_method :source, :html
@@ -238,13 +242,13 @@ module Capybara
238
242
  #
239
243
  # For drivers which can run against an external application, such as the selenium driver
240
244
  # giving an absolute URL will navigate to that page. This allows testing applications
241
- # running on remote servers. For these drivers, setting {Capybara.app_host} will make the
245
+ # running on remote servers. For these drivers, setting {Capybara.configure app_host} will make the
242
246
  # remote server the default. For example:
243
247
  #
244
248
  # Capybara.app_host = 'http://google.com'
245
249
  # session.visit('/') # visits the google homepage
246
250
  #
247
- # If {Capybara.always_include_port} is set to true and this session is running against
251
+ # If {Capybara.configure always_include_port} is set to `true` and this session is running against
248
252
  # a rack application, then the port that the rack application is running on will automatically
249
253
  # be inserted into the URL. Supposing the app is running on port `4567`, doing something like:
250
254
  #
@@ -263,7 +267,7 @@ module Capybara
263
267
 
264
268
  if base_uri && [nil, 'http', 'https'].include?(visit_uri.scheme)
265
269
  if visit_uri.relative?
266
- visit_uri_parts = visit_uri.to_hash.delete_if { |_k, value| value.nil? }
270
+ visit_uri_parts = visit_uri.to_hash.compact
267
271
 
268
272
  # Useful to people deploying to a subdirectory
269
273
  # and/or single page apps where only the url fragment changes
@@ -279,7 +283,7 @@ module Capybara
279
283
 
280
284
  ##
281
285
  #
282
- # Refresh the page
286
+ # Refresh the page.
283
287
  #
284
288
  def refresh
285
289
  raise_server_error!
@@ -302,10 +306,28 @@ module Capybara
302
306
  driver.go_forward
303
307
  end
304
308
 
309
+ ##
310
+ # @!method send_keys
311
+ # @see Capybara::Node::Element#send_keys
312
+ #
313
+ def send_keys(...)
314
+ driver.send_keys(...)
315
+ end
316
+
317
+ ##
318
+ #
319
+ # Returns the element with focus.
320
+ #
321
+ # Not supported by Rack Test
322
+ #
323
+ def active_element
324
+ Capybara::Queries::ActiveElementQuery.new.resolve_for(self)[0].tap(&:allow_reload!)
325
+ end
326
+
305
327
  ##
306
328
  #
307
- # Executes the given block within the context of a node. `within` takes the
308
- # same options as `find`, as well as a block. For the duration of the
329
+ # Executes the given block within the context of a node. {#within} takes the
330
+ # same options as {Capybara::Node::Finders#find #find}, as well as a block. For the duration of the
309
331
  # block, any command to Capybara will be handled as though it were scoped
310
332
  # to the given element.
311
333
  #
@@ -313,18 +335,18 @@ module Capybara
313
335
  # fill_in('Street', with: '12 Main Street')
314
336
  # end
315
337
  #
316
- # Just as with `find`, if multiple elements match the selector given to
317
- # `within`, an error will be raised, and just as with `find`, this
338
+ # Just as with `#find`, if multiple elements match the selector given to
339
+ # {#within}, an error will be raised, and just as with `#find`, this
318
340
  # behaviour can be controlled through the `:match` and `:exact` options.
319
341
  #
320
342
  # It is possible to omit the first parameter, in that case, the selector is
321
- # assumed to be of the type set in Capybara.default_selector.
343
+ # assumed to be of the type set in {Capybara.configure default_selector}.
322
344
  #
323
345
  # within('div#delivery-address') do
324
346
  # fill_in('Street', with: '12 Main Street')
325
347
  # end
326
348
  #
327
- # Note that a lot of uses of `within` can be replaced more succinctly with
349
+ # Note that a lot of uses of {#within} can be replaced more succinctly with
328
350
  # chaining:
329
351
  #
330
352
  # find('div#delivery-address').fill_in('Street', with: '12 Main Street')
@@ -337,11 +359,11 @@ module Capybara
337
359
  #
338
360
  # @raise [Capybara::ElementNotFound] If the scope can't be found before time expires
339
361
  #
340
- def within(*args)
341
- new_scope = args.first.respond_to?(:to_capybara_node) ? args.first.to_capybara_node : find(*args)
362
+ def within(*args, **kw_args)
363
+ new_scope = args.first.respond_to?(:to_capybara_node) ? args.first.to_capybara_node : find(*args, **kw_args)
342
364
  begin
343
365
  scopes.push(new_scope)
344
- yield if block_given?
366
+ yield new_scope if block_given?
345
367
  ensure
346
368
  scopes.pop
347
369
  end
@@ -354,8 +376,8 @@ module Capybara
354
376
  #
355
377
  # @param [String] locator Id or legend of the fieldset
356
378
  #
357
- def within_fieldset(locator)
358
- within(:fieldset, locator) { yield }
379
+ def within_fieldset(locator, &block)
380
+ within(:fieldset, locator, &block)
359
381
  end
360
382
 
361
383
  ##
@@ -364,24 +386,24 @@ module Capybara
364
386
  #
365
387
  # @param [String] locator Id or caption of the table
366
388
  #
367
- def within_table(locator)
368
- within(:table, locator) { yield }
389
+ def within_table(locator, &block)
390
+ within(:table, locator, &block)
369
391
  end
370
392
 
371
393
  ##
372
394
  #
373
- # Switch to the given frame
395
+ # Switch to the given frame.
374
396
  #
375
397
  # If you use this method you are responsible for making sure you switch back to the parent frame when done in the frame changed to.
376
- # Capybara::Session#within_frame is preferred over this method and should be used when possible.
398
+ # {#within_frame} is preferred over this method and should be used when possible.
377
399
  # May not be supported by all drivers.
378
400
  #
379
401
  # @overload switch_to_frame(element)
380
- # @param [Capybara::Node::Element] iframe/frame element to switch to
381
- # @overload switch_to_frame(:parent)
382
- # Switch to the parent frame
383
- # @overload switch_to_frame(:top)
384
- # Switch to the top level document
402
+ # @param [Capybara::Node::Element] element iframe/frame element to switch to
403
+ # @overload switch_to_frame(location)
404
+ # @param [Symbol] location relative location of the frame to switch to
405
+ # * :parent - the parent frame
406
+ # * :top - the top level document
385
407
  #
386
408
  def switch_to_frame(frame)
387
409
  case frame
@@ -390,19 +412,20 @@ module Capybara
390
412
  scopes.push(:frame)
391
413
  when :parent
392
414
  if scopes.last != :frame
393
- raise Capybara::ScopeError, "`switch_to_frame(:parent)` cannot be called from inside a descendant frame's "\
415
+ raise Capybara::ScopeError, "`switch_to_frame(:parent)` cannot be called from inside a descendant frame's " \
394
416
  '`within` block.'
395
417
  end
396
418
  scopes.pop
397
419
  driver.switch_to_frame(:parent)
398
420
  when :top
399
421
  idx = scopes.index(:frame)
422
+ top_level_scopes = [:frame, nil]
400
423
  if idx
401
- if scopes.slice(idx..-1).any? { |scope| ![:frame, nil].include?(scope) }
402
- raise Capybara::ScopeError, "`switch_to_frame(:top)` cannot be called from inside a descendant frame's "\
424
+ if scopes.slice(idx..).any? { |scope| !top_level_scopes.include?(scope) }
425
+ raise Capybara::ScopeError, "`switch_to_frame(:top)` cannot be called from inside a descendant frame's " \
403
426
  '`within` block.'
404
427
  end
405
- scopes.slice!(idx..-1)
428
+ scopes.slice!(idx..)
406
429
  driver.switch_to_frame(:top)
407
430
  end
408
431
  else
@@ -422,8 +445,8 @@ module Capybara
422
445
  # @param [String] locator The locator for the given selector kind. For :frame this is the name/id of a frame/iframe element
423
446
  # @overload within_frame(index)
424
447
  # @param [Integer] index index of a frame (0 based)
425
- def within_frame(*args)
426
- switch_to_frame(_find_frame(*args))
448
+ def within_frame(*args, **kw_args)
449
+ switch_to_frame(_find_frame(*args, **kw_args))
427
450
  begin
428
451
  yield if block_given?
429
452
  ensure
@@ -452,22 +475,28 @@ module Capybara
452
475
  end
453
476
 
454
477
  ##
455
- # Open new window.
456
- # Current window doesn't change as the result of this call.
478
+ # Open a new window.
479
+ # The current window doesn't change as the result of this call.
457
480
  # It should be switched to explicitly.
458
481
  #
459
482
  # @return [Capybara::Window] window that has been opened
460
483
  #
461
- def open_new_window
484
+ def open_new_window(kind = :tab)
462
485
  window_opened_by do
463
- driver.open_new_window
486
+ if driver.method(:open_new_window).arity.zero?
487
+ driver.open_new_window
488
+ else
489
+ driver.open_new_window(kind)
490
+ end
464
491
  end
465
492
  end
466
493
 
467
494
  ##
495
+ # Switch to the given window.
496
+ #
468
497
  # @overload switch_to_window(&block)
469
498
  # Switches to the first window for which given block returns a value other than false or nil.
470
- # If window that matches block can't be found, the window will be switched back and `WindowError` will be raised.
499
+ # If window that matches block can't be found, the window will be switched back and {Capybara::WindowError} will be raised.
471
500
  # @example
472
501
  # window = switch_to_window { title == 'Page title' }
473
502
  # @raise [Capybara::WindowError] if no window matches given block
@@ -476,20 +505,20 @@ module Capybara
476
505
  # @raise [Capybara::Driver::Base#no_such_window_error] if nonexistent (e.g. closed) window was passed
477
506
  #
478
507
  # @return [Capybara::Window] window that has been switched to
479
- # @raise [Capybara::ScopeError] if this method is invoked inside `within` or
480
- # `within_frame` methods
508
+ # @raise [Capybara::ScopeError] if this method is invoked inside {#within} or
509
+ # {#within_frame} methods
481
510
  # @raise [ArgumentError] if both or neither arguments were provided
482
511
  #
483
512
  def switch_to_window(window = nil, **options, &window_locator)
484
- raise ArgumentError, '`switch_to_window` can take either a block or a window, not both' if window && block_given?
485
- raise ArgumentError, '`switch_to_window`: either window or block should be provided' if !window && !block_given?
513
+ raise ArgumentError, '`switch_to_window` can take either a block or a window, not both' if window && window_locator
514
+ raise ArgumentError, '`switch_to_window`: either window or block should be provided' if !window && !window_locator
486
515
 
487
516
  unless scopes.last.nil?
488
- raise Capybara::ScopeError, '`switch_to_window` is not supposed to be invoked from '\
517
+ raise Capybara::ScopeError, '`switch_to_window` is not supposed to be invoked from ' \
489
518
  '`within` or `within_frame` blocks.'
490
519
  end
491
520
 
492
- _switch_to_window(window, options, &window_locator)
521
+ _switch_to_window(window, **options, &window_locator)
493
522
  end
494
523
 
495
524
  ##
@@ -497,20 +526,20 @@ module Capybara
497
526
  #
498
527
  # 1. Switches to the given window (it can be located by window instance/lambda/string).
499
528
  # 2. Executes the given block (within window located at previous step).
500
- # 3. Switches back (this step will be invoked even if exception will happen at second step)
529
+ # 3. Switches back (this step will be invoked even if an exception occurs at the second step).
501
530
  #
502
531
  # @overload within_window(window) { do_something }
503
- # @param window [Capybara::Window] instance of `Capybara::Window` class
532
+ # @param window [Capybara::Window] instance of {Capybara::Window} class
504
533
  # that will be switched to
505
534
  # @raise [driver#no_such_window_error] if nonexistent (e.g. closed) window was passed
506
535
  # @overload within_window(proc_or_lambda) { do_something }
507
- # @param lambda [Proc] lambda. First window for which lambda
536
+ # @param lambda [Proc] First window for which lambda
508
537
  # returns a value other than false or nil will be switched to.
509
538
  # @example
510
539
  # within_window(->{ page.title == 'Page title' }) { click_button 'Submit' }
511
540
  # @raise [Capybara::WindowError] if no window matching lambda was found
512
541
  #
513
- # @raise [Capybara::ScopeError] if this method is invoked inside `within_frame` method
542
+ # @raise [Capybara::ScopeError] if this method is invoked inside {#within_frame} method
514
543
  # @return value returned by the block
515
544
  #
516
545
  def within_window(window_or_proc)
@@ -523,7 +552,7 @@ module Capybara
523
552
  when Proc
524
553
  _switch_to_window { window_or_proc.call }
525
554
  else
526
- raise ArgumentError('`#within_window` requires a `Capybara::Window` instance or a lambda')
555
+ raise ArgumentError, '`#within_window` requires a `Capybara::Window` instance or a lambda'
527
556
  end
528
557
 
529
558
  begin
@@ -540,11 +569,11 @@ module Capybara
540
569
  # Get the window that has been opened by the passed block.
541
570
  # It will wait for it to be opened (in the same way as other Capybara methods wait).
542
571
  # It's better to use this method than `windows.last`
543
- # {https://dvcs.w3.org/hg/webdriver/raw-file/default/webdriver-spec.html#h_note_10 as order of windows isn't defined in some drivers}
572
+ # {https://dvcs.w3.org/hg/webdriver/raw-file/default/webdriver-spec.html#h_note_10 as order of windows isn't defined in some drivers}.
544
573
  #
545
574
  # @overload window_opened_by(**options, &block)
546
575
  # @param options [Hash]
547
- # @option options [Numeric] :wait (Capybara.default_max_wait_time) maximum wait time
576
+ # @option options [Numeric] :wait maximum wait time. Defaults to {Capybara.configure default_max_wait_time}
548
577
  # @return [Capybara::Window] the window that has been opened within a block
549
578
  # @raise [Capybara::WindowError] if block passed to window hasn't opened window
550
579
  # or opened more than one window
@@ -556,7 +585,7 @@ module Capybara
556
585
  synchronize_windows(options) do
557
586
  opened_handles = (driver.window_handles - old_handles)
558
587
  if opened_handles.size != 1
559
- raise Capybara::WindowError, 'block passed to #window_opened_by '\
588
+ raise Capybara::WindowError, 'block passed to #window_opened_by ' \
560
589
  "opened #{opened_handles.size} windows instead of 1"
561
590
  end
562
591
  Window.new(self, opened_handles.first)
@@ -566,11 +595,11 @@ module Capybara
566
595
  ##
567
596
  #
568
597
  # Execute the given script, not returning a result. This is useful for scripts that return
569
- # complex objects, such as jQuery statements. +execute_script+ should be used over
570
- # +evaluate_script+ whenever possible.
598
+ # complex objects, such as jQuery statements. {#execute_script} should be used over
599
+ # {#evaluate_script} whenever possible.
571
600
  #
572
601
  # @param [String] script A string of JavaScript to execute
573
- # @param args Optional arguments that will be passed to the script. Driver support for this is optional and types of objects supported may differ between drivers
602
+ # @param args Optional arguments that will be passed to the script. Driver support for this is optional and types of objects supported may differ between drivers
574
603
  #
575
604
  def execute_script(script, *args)
576
605
  @touched = true
@@ -580,10 +609,11 @@ module Capybara
580
609
  ##
581
610
  #
582
611
  # Evaluate the given JavaScript and return the result. Be careful when using this with
583
- # scripts that return complex objects, such as jQuery statements. +execute_script+ might
612
+ # scripts that return complex objects, such as jQuery statements. {#execute_script} might
584
613
  # be a better alternative.
585
614
  #
586
615
  # @param [String] script A string of JavaScript to evaluate
616
+ # @param args Optional arguments that will be passed to the script
587
617
  # @return [Object] The result of the evaluated JavaScript (may be driver specific)
588
618
  #
589
619
  def evaluate_script(script, *args)
@@ -597,6 +627,7 @@ module Capybara
597
627
  # Evaluate the given JavaScript and obtain the result from a callback function which will be passed as the last argument to the script.
598
628
  #
599
629
  # @param [String] script A string of JavaScript to evaluate
630
+ # @param args Optional arguments that will be passed to the script
600
631
  # @return [Object] The result of the evaluated JavaScript (may be driver specific)
601
632
  #
602
633
  def evaluate_async_script(script, *args)
@@ -610,17 +641,17 @@ module Capybara
610
641
  # Execute the block, accepting a alert.
611
642
  #
612
643
  # @!macro modal_params
613
- # Expects a block whose actions will trigger the display modal to appear
644
+ # Expects a block whose actions will trigger the display modal to appear.
614
645
  # @example
615
646
  # $0 do
616
647
  # click_link('link that triggers appearance of system modal')
617
648
  # end
618
649
  # @overload $0(text, **options, &blk)
619
- # @param text [String, Regexp] Text or regex to match against the text in the modal. If not provided any modal is matched
620
- # @option options [Numeric] :wait (Capybara.default_max_wait_time) Maximum time to wait for the modal to appear after executing the block.
650
+ # @param text [String, Regexp] Text or regex to match against the text in the modal. If not provided any modal is matched.
651
+ # @option options [Numeric] :wait Maximum time to wait for the modal to appear after executing the block. Defaults to {Capybara.configure default_max_wait_time}.
621
652
  # @yield Block whose actions will trigger the system modal
622
653
  # @overload $0(**options, &blk)
623
- # @option options [Numeric] :wait (Capybara.default_max_wait_time) Maximum time to wait for the modal to appear after executing the block.
654
+ # @option options [Numeric] :wait Maximum time to wait for the modal to appear after executing the block. Defaults to {Capybara.configure default_max_wait_time}.
624
655
  # @yield Block whose actions will trigger the system modal
625
656
  # @return [String] the message shown in the modal
626
657
  # @raise [Capybara::ModalNotFound] if modal dialog hasn't been found
@@ -672,12 +703,12 @@ module Capybara
672
703
 
673
704
  ##
674
705
  #
675
- # Save a snapshot of the page. If `Capybara.asset_host` is set it will inject `base` tag
676
- # pointing to `asset_host`.
706
+ # Save a snapshot of the page. If {Capybara.configure asset_host} is set it will inject `base` tag
707
+ # pointing to {Capybara.configure asset_host}.
677
708
  #
678
- # If invoked without arguments it will save file to `Capybara.save_path`
679
- # and file will be given randomly generated filename. If invoked with a relative path
680
- # the path will be relative to `Capybara.save_path`
709
+ # If invoked without arguments it will save file to {Capybara.configure save_path}
710
+ # and file will be given randomly generated filename. If invoked with a relative path
711
+ # the path will be relative to {Capybara.configure save_path}.
681
712
  #
682
713
  # @param [String] path the path to where it should be saved
683
714
  # @return [String] the path to which the file was saved
@@ -692,9 +723,9 @@ module Capybara
692
723
  #
693
724
  # Save a snapshot of the page and open it in a browser for inspection.
694
725
  #
695
- # If invoked without arguments it will save file to `Capybara.save_path`
696
- # and file will be given randomly generated filename. If invoked with a relative path
697
- # the path will be relative to `Capybara.save_path`
726
+ # If invoked without arguments it will save file to {Capybara.configure save_path}
727
+ # and file will be given randomly generated filename. If invoked with a relative path
728
+ # the path will be relative to {Capybara.configure save_path}.
698
729
  #
699
730
  # @param [String] path the path to where it should be saved
700
731
  #
@@ -706,32 +737,30 @@ module Capybara
706
737
  #
707
738
  # Save a screenshot of page.
708
739
  #
709
- # If invoked without arguments it will save file to `Capybara.save_path`
710
- # and file will be given randomly generated filename. If invoked with a relative path
711
- # the path will be relative to `Capybara.save_path`
740
+ # If invoked without arguments it will save file to {Capybara.configure save_path}
741
+ # and file will be given randomly generated filename. If invoked with a relative path
742
+ # the path will be relative to {Capybara.configure save_path}.
712
743
  #
713
744
  # @param [String] path the path to where it should be saved
714
745
  # @param [Hash] options a customizable set of options
715
746
  # @return [String] the path to which the file was saved
716
747
  def save_screenshot(path = nil, **options)
717
- prepare_path(path, 'png').tap { |p_path| driver.save_screenshot(p_path, options) }
748
+ prepare_path(path, 'png').tap { |p_path| driver.save_screenshot(p_path, **options) }
718
749
  end
719
750
 
720
751
  ##
721
752
  #
722
753
  # Save a screenshot of the page and open it for inspection.
723
754
  #
724
- # If invoked without arguments it will save file to `Capybara.save_path`
725
- # and file will be given randomly generated filename. If invoked with a relative path
726
- # the path will be relative to `Capybara.save_path`
755
+ # If invoked without arguments it will save file to {Capybara.configure save_path}
756
+ # and file will be given randomly generated filename. If invoked with a relative path
757
+ # the path will be relative to {Capybara.configure save_path}.
727
758
  #
728
759
  # @param [String] path the path to where it should be saved
729
760
  # @param [Hash] options a customizable set of options
730
761
  #
731
762
  def save_and_open_screenshot(path = nil, **options)
732
- # rubocop:disable Lint/Debugger
733
- save_screenshot(path, options).tap { |s_path| open_file(s_path) }
734
- # rubocop:enable Lint/Debugger
763
+ save_screenshot(path, **options).tap { |s_path| open_file(s_path) }
735
764
  end
736
765
 
737
766
  def document
@@ -739,16 +768,20 @@ module Capybara
739
768
  end
740
769
 
741
770
  NODE_METHODS.each do |method|
742
- define_method method do |*args, &block|
743
- @touched = true
744
- current_scope.send(method, *args, &block)
745
- end
771
+ class_eval <<~METHOD, __FILE__, __LINE__ + 1
772
+ def #{method}(...)
773
+ @touched = true
774
+ current_scope.#{method}(...)
775
+ end
776
+ METHOD
746
777
  end
747
778
 
748
779
  DOCUMENT_METHODS.each do |method|
749
- define_method method do |*args, &block|
750
- document.send(method, *args, &block)
751
- end
780
+ class_eval <<~METHOD, __FILE__, __LINE__ + 1
781
+ def #{method}(...)
782
+ document.#{method}(...)
783
+ end
784
+ METHOD
752
785
  end
753
786
 
754
787
  def inspect
@@ -762,9 +795,9 @@ module Capybara
762
795
 
763
796
  ##
764
797
  #
765
- # Yield a block using a specific wait time
798
+ # Yield a block using a specific maximum wait time.
766
799
  #
767
- def using_wait_time(seconds)
800
+ def using_wait_time(seconds, &block)
768
801
  if Capybara.threadsafe
769
802
  begin
770
803
  previous_wait_time = config.default_max_wait_time
@@ -774,14 +807,14 @@ module Capybara
774
807
  config.default_max_wait_time = previous_wait_time
775
808
  end
776
809
  else
777
- Capybara.using_wait_time(seconds) { yield }
810
+ Capybara.using_wait_time(seconds, &block)
778
811
  end
779
812
  end
780
813
 
781
814
  ##
782
815
  #
783
- # Accepts a block to set the configuration options if Capybara.threadsafe == true. Note that some options only have an effect
784
- # if set at initialization time, so look at the configuration block that can be passed to the initializer too
816
+ # Accepts a block to set the configuration options if {Capybara.configure threadsafe} is `true`. Note that some options only have an effect
817
+ # if set at initialization time, so look at the configuration block that can be passed to the initializer too.
785
818
  #
786
819
  def configure
787
820
  raise 'Session configuration is only supported when Capybara.threadsafe == true' unless Capybara.threadsafe
@@ -801,20 +834,24 @@ module Capybara
801
834
  end
802
835
  end
803
836
 
837
+ def server_url
838
+ @server&.base_url
839
+ end
840
+
804
841
  private
805
842
 
806
- @@instance_created = false
843
+ @@instance_created = false # rubocop:disable Style/ClassVars
807
844
 
808
845
  def driver_args(args)
809
846
  args.map { |arg| arg.is_a?(Capybara::Node::Element) ? arg.base : arg }
810
847
  end
811
848
 
812
849
  def accept_modal(type, text_or_options, options, &blk)
813
- driver.accept_modal(type, modal_options(text_or_options, options), &blk)
850
+ driver.accept_modal(type, **modal_options(text_or_options, **options), &blk)
814
851
  end
815
852
 
816
853
  def dismiss_modal(type, text_or_options, options, &blk)
817
- driver.dismiss_modal(type, modal_options(text_or_options, options), &blk)
854
+ driver.dismiss_modal(type, **modal_options(text_or_options, **options), &blk)
818
855
  end
819
856
 
820
857
  def modal_options(text = nil, **options)
@@ -850,7 +887,7 @@ module Capybara
850
887
  when Array
851
888
  arg.map { |subarg| element_script_result(subarg) }
852
889
  when Hash
853
- arg.each { |key, value| arg[key] = element_script_result(value) }
890
+ arg.transform_values! { |value| element_script_result(value) }
854
891
  when Capybara::Driver::Node
855
892
  Capybara::Node::Element.new(self, arg, nil, nil)
856
893
  else
@@ -858,24 +895,18 @@ module Capybara
858
895
  end
859
896
  end
860
897
 
861
- def server_url
862
- "http#{'s' if @server.using_ssl?}://#{@server.host}:#{@server.port}" if @server
863
- end
864
-
865
898
  def adjust_server_port(uri)
866
899
  uri.port ||= @server.port if @server && config.always_include_port
867
900
  end
868
901
 
869
- def _find_frame(*args)
870
- return find(:frame) if args.length.zero?
871
-
902
+ def _find_frame(*args, **kw_args)
872
903
  case args[0]
873
904
  when Capybara::Node::Element
874
905
  args[0]
875
- when String, Hash
876
- find(:frame, *args)
906
+ when String, nil
907
+ find(:frame, *args, **kw_args)
877
908
  when Symbol
878
- find(*args)
909
+ find(*args, **kw_args)
879
910
  when Integer
880
911
  idx = args[0]
881
912
  all(:frame, minimum: idx + 1)[idx]