capybara 3.0.0 → 3.36.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (308) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/History.md +777 -10
  4. data/License.txt +1 -1
  5. data/README.md +75 -58
  6. data/lib/capybara/config.rb +29 -10
  7. data/lib/capybara/cucumber.rb +1 -1
  8. data/lib/capybara/driver/base.rb +22 -4
  9. data/lib/capybara/driver/node.rb +34 -9
  10. data/lib/capybara/dsl.rb +14 -6
  11. data/lib/capybara/helpers.rb +52 -7
  12. data/lib/capybara/minitest/spec.rb +173 -84
  13. data/lib/capybara/minitest.rb +250 -144
  14. data/lib/capybara/node/actions.rb +248 -124
  15. data/lib/capybara/node/base.rb +34 -20
  16. data/lib/capybara/node/document.rb +14 -2
  17. data/lib/capybara/node/document_matchers.rb +13 -15
  18. data/lib/capybara/node/element.rb +339 -113
  19. data/lib/capybara/node/finders.rb +95 -82
  20. data/lib/capybara/node/matchers.rb +338 -157
  21. data/lib/capybara/node/simple.rb +54 -15
  22. data/lib/capybara/queries/active_element_query.rb +18 -0
  23. data/lib/capybara/queries/ancestor_query.rb +9 -10
  24. data/lib/capybara/queries/base_query.rb +25 -18
  25. data/lib/capybara/queries/current_path_query.rb +16 -6
  26. data/lib/capybara/queries/match_query.rb +11 -0
  27. data/lib/capybara/queries/selector_query.rb +600 -103
  28. data/lib/capybara/queries/sibling_query.rb +9 -7
  29. data/lib/capybara/queries/style_query.rb +45 -0
  30. data/lib/capybara/queries/text_query.rb +40 -22
  31. data/lib/capybara/queries/title_query.rb +2 -2
  32. data/lib/capybara/rack_test/browser.rb +47 -28
  33. data/lib/capybara/rack_test/driver.rb +12 -3
  34. data/lib/capybara/rack_test/errors.rb +6 -0
  35. data/lib/capybara/rack_test/form.rb +53 -50
  36. data/lib/capybara/rack_test/node.rb +114 -37
  37. data/lib/capybara/rails.rb +1 -1
  38. data/lib/capybara/registration_container.rb +44 -0
  39. data/lib/capybara/registrations/drivers.rb +42 -0
  40. data/lib/capybara/registrations/patches/puma_ssl.rb +29 -0
  41. data/lib/capybara/registrations/servers.rb +45 -0
  42. data/lib/capybara/result.rb +86 -52
  43. data/lib/capybara/rspec/features.rb +8 -10
  44. data/lib/capybara/rspec/matcher_proxies.rb +39 -18
  45. data/lib/capybara/rspec/matchers/base.rb +111 -0
  46. data/lib/capybara/rspec/matchers/become_closed.rb +33 -0
  47. data/lib/capybara/rspec/matchers/compound.rb +88 -0
  48. data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
  49. data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
  50. data/lib/capybara/rspec/matchers/have_current_path.rb +29 -0
  51. data/lib/capybara/rspec/matchers/have_selector.rb +77 -0
  52. data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
  53. data/lib/capybara/rspec/matchers/have_text.rb +33 -0
  54. data/lib/capybara/rspec/matchers/have_title.rb +29 -0
  55. data/lib/capybara/rspec/matchers/match_selector.rb +27 -0
  56. data/lib/capybara/rspec/matchers/match_style.rb +43 -0
  57. data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
  58. data/lib/capybara/rspec/matchers.rb +138 -316
  59. data/lib/capybara/rspec.rb +3 -2
  60. data/lib/capybara/selector/builders/css_builder.rb +84 -0
  61. data/lib/capybara/selector/builders/xpath_builder.rb +71 -0
  62. data/lib/capybara/selector/css.rb +89 -12
  63. data/lib/capybara/selector/definition/button.rb +68 -0
  64. data/lib/capybara/selector/definition/checkbox.rb +26 -0
  65. data/lib/capybara/selector/definition/css.rb +10 -0
  66. data/lib/capybara/selector/definition/datalist_input.rb +35 -0
  67. data/lib/capybara/selector/definition/datalist_option.rb +25 -0
  68. data/lib/capybara/selector/definition/element.rb +28 -0
  69. data/lib/capybara/selector/definition/field.rb +40 -0
  70. data/lib/capybara/selector/definition/fieldset.rb +14 -0
  71. data/lib/capybara/selector/definition/file_field.rb +13 -0
  72. data/lib/capybara/selector/definition/fillable_field.rb +33 -0
  73. data/lib/capybara/selector/definition/frame.rb +17 -0
  74. data/lib/capybara/selector/definition/id.rb +6 -0
  75. data/lib/capybara/selector/definition/label.rb +62 -0
  76. data/lib/capybara/selector/definition/link.rb +54 -0
  77. data/lib/capybara/selector/definition/link_or_button.rb +16 -0
  78. data/lib/capybara/selector/definition/option.rb +27 -0
  79. data/lib/capybara/selector/definition/radio_button.rb +27 -0
  80. data/lib/capybara/selector/definition/select.rb +81 -0
  81. data/lib/capybara/selector/definition/table.rb +109 -0
  82. data/lib/capybara/selector/definition/table_row.rb +21 -0
  83. data/lib/capybara/selector/definition/xpath.rb +5 -0
  84. data/lib/capybara/selector/definition.rb +279 -0
  85. data/lib/capybara/selector/filter.rb +1 -0
  86. data/lib/capybara/selector/filter_set.rb +72 -27
  87. data/lib/capybara/selector/filters/base.rb +45 -2
  88. data/lib/capybara/selector/filters/expression_filter.rb +5 -6
  89. data/lib/capybara/selector/filters/locator_filter.rb +29 -0
  90. data/lib/capybara/selector/filters/node_filter.rb +18 -4
  91. data/lib/capybara/selector/regexp_disassembler.rb +214 -0
  92. data/lib/capybara/selector/selector.rb +85 -200
  93. data/lib/capybara/selector/xpath_extensions.rb +17 -0
  94. data/lib/capybara/selector.rb +226 -537
  95. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -0
  96. data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -0
  97. data/lib/capybara/selenium/atoms/src/getAttribute.js +161 -0
  98. data/lib/capybara/selenium/atoms/src/isDisplayed.js +454 -0
  99. data/lib/capybara/selenium/driver.rb +296 -238
  100. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +117 -0
  101. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +124 -0
  102. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +89 -0
  103. data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +26 -0
  104. data/lib/capybara/selenium/driver_specializations/safari_driver.rb +24 -0
  105. data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
  106. data/lib/capybara/selenium/extensions/find.rb +110 -0
  107. data/lib/capybara/selenium/extensions/html5_drag.rb +228 -0
  108. data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
  109. data/lib/capybara/selenium/extensions/scroll.rb +76 -0
  110. data/lib/capybara/selenium/logger_suppressor.rb +40 -0
  111. data/lib/capybara/selenium/node.rb +436 -166
  112. data/lib/capybara/selenium/nodes/chrome_node.rb +137 -0
  113. data/lib/capybara/selenium/nodes/edge_node.rb +104 -0
  114. data/lib/capybara/selenium/nodes/firefox_node.rb +136 -0
  115. data/lib/capybara/selenium/nodes/ie_node.rb +22 -0
  116. data/lib/capybara/selenium/nodes/safari_node.rb +118 -0
  117. data/lib/capybara/selenium/patches/action_pauser.rb +26 -0
  118. data/lib/capybara/selenium/patches/atoms.rb +18 -0
  119. data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
  120. data/lib/capybara/selenium/patches/logs.rb +45 -0
  121. data/lib/capybara/selenium/patches/pause_duration_fix.rb +9 -0
  122. data/lib/capybara/selenium/patches/persistent_client.rb +20 -0
  123. data/lib/capybara/server/animation_disabler.rb +69 -0
  124. data/lib/capybara/server/checker.rb +44 -0
  125. data/lib/capybara/server/middleware.rb +71 -0
  126. data/lib/capybara/server.rb +58 -67
  127. data/lib/capybara/session/config.rb +38 -6
  128. data/lib/capybara/session/matchers.rb +26 -19
  129. data/lib/capybara/session.rb +258 -190
  130. data/lib/capybara/spec/public/jquery.js +5 -5
  131. data/lib/capybara/spec/public/offset.js +6 -0
  132. data/lib/capybara/spec/public/test.js +122 -8
  133. data/lib/capybara/spec/session/accept_alert_spec.rb +11 -11
  134. data/lib/capybara/spec/session/accept_confirm_spec.rb +3 -3
  135. data/lib/capybara/spec/session/accept_prompt_spec.rb +9 -10
  136. data/lib/capybara/spec/session/active_element_spec.rb +31 -0
  137. data/lib/capybara/spec/session/all_spec.rb +135 -42
  138. data/lib/capybara/spec/session/ancestor_spec.rb +24 -19
  139. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +67 -38
  140. data/lib/capybara/spec/session/{assert_current_path.rb → assert_current_path_spec.rb} +20 -18
  141. data/lib/capybara/spec/session/assert_selector_spec.rb +143 -0
  142. data/lib/capybara/spec/session/assert_style_spec.rb +26 -0
  143. data/lib/capybara/spec/session/{assert_text.rb → assert_text_spec.rb} +62 -38
  144. data/lib/capybara/spec/session/{assert_title.rb → assert_title_spec.rb} +12 -12
  145. data/lib/capybara/spec/session/attach_file_spec.rb +120 -72
  146. data/lib/capybara/spec/session/body_spec.rb +11 -13
  147. data/lib/capybara/spec/session/check_spec.rb +111 -51
  148. data/lib/capybara/spec/session/choose_spec.rb +62 -30
  149. data/lib/capybara/spec/session/click_button_spec.rb +227 -161
  150. data/lib/capybara/spec/session/click_link_or_button_spec.rb +49 -30
  151. data/lib/capybara/spec/session/click_link_spec.rb +78 -55
  152. data/lib/capybara/spec/session/current_scope_spec.rb +7 -7
  153. data/lib/capybara/spec/session/current_url_spec.rb +44 -37
  154. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +3 -3
  155. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +2 -2
  156. data/lib/capybara/spec/session/element/{assert_match_selector.rb → assert_match_selector_spec.rb} +11 -9
  157. data/lib/capybara/spec/session/element/match_css_spec.rb +18 -10
  158. data/lib/capybara/spec/session/element/match_xpath_spec.rb +8 -6
  159. data/lib/capybara/spec/session/element/matches_selector_spec.rb +70 -56
  160. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +7 -7
  161. data/lib/capybara/spec/session/evaluate_script_spec.rb +28 -8
  162. data/lib/capybara/spec/session/execute_script_spec.rb +8 -7
  163. data/lib/capybara/spec/session/fill_in_spec.rb +104 -44
  164. data/lib/capybara/spec/session/find_button_spec.rb +23 -23
  165. data/lib/capybara/spec/session/find_by_id_spec.rb +8 -8
  166. data/lib/capybara/spec/session/find_field_spec.rb +33 -31
  167. data/lib/capybara/spec/session/find_link_spec.rb +32 -14
  168. data/lib/capybara/spec/session/find_spec.rb +236 -141
  169. data/lib/capybara/spec/session/first_spec.rb +44 -43
  170. data/lib/capybara/spec/session/frame/frame_title_spec.rb +6 -6
  171. data/lib/capybara/spec/session/frame/frame_url_spec.rb +6 -6
  172. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +32 -20
  173. data/lib/capybara/spec/session/frame/within_frame_spec.rb +44 -19
  174. data/lib/capybara/spec/session/go_back_spec.rb +1 -1
  175. data/lib/capybara/spec/session/go_forward_spec.rb +1 -1
  176. data/lib/capybara/spec/session/has_all_selectors_spec.rb +18 -18
  177. data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
  178. data/lib/capybara/spec/session/has_any_selectors_spec.rb +29 -0
  179. data/lib/capybara/spec/session/has_button_spec.rb +92 -12
  180. data/lib/capybara/spec/session/has_css_spec.rb +271 -137
  181. data/lib/capybara/spec/session/has_current_path_spec.rb +48 -33
  182. data/lib/capybara/spec/session/has_field_spec.rb +137 -58
  183. data/lib/capybara/spec/session/has_link_spec.rb +30 -6
  184. data/lib/capybara/spec/session/has_none_selectors_spec.rb +26 -24
  185. data/lib/capybara/spec/session/has_select_spec.rb +75 -47
  186. data/lib/capybara/spec/session/has_selector_spec.rb +102 -69
  187. data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
  188. data/lib/capybara/spec/session/has_table_spec.rb +170 -4
  189. data/lib/capybara/spec/session/has_text_spec.rb +108 -52
  190. data/lib/capybara/spec/session/has_title_spec.rb +19 -14
  191. data/lib/capybara/spec/session/has_xpath_spec.rb +56 -38
  192. data/lib/capybara/spec/session/{headers.rb → headers_spec.rb} +1 -1
  193. data/lib/capybara/spec/session/html_spec.rb +13 -6
  194. data/lib/capybara/spec/session/matches_style_spec.rb +35 -0
  195. data/lib/capybara/spec/session/node_spec.rb +871 -122
  196. data/lib/capybara/spec/session/node_wrapper_spec.rb +15 -12
  197. data/lib/capybara/spec/session/refresh_spec.rb +9 -7
  198. data/lib/capybara/spec/session/reset_session_spec.rb +52 -37
  199. data/lib/capybara/spec/session/{response_code.rb → response_code_spec.rb} +1 -1
  200. data/lib/capybara/spec/session/save_and_open_page_spec.rb +2 -2
  201. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +5 -4
  202. data/lib/capybara/spec/session/save_page_spec.rb +41 -38
  203. data/lib/capybara/spec/session/save_screenshot_spec.rb +13 -11
  204. data/lib/capybara/spec/session/screenshot_spec.rb +2 -2
  205. data/lib/capybara/spec/session/scroll_spec.rb +117 -0
  206. data/lib/capybara/spec/session/select_spec.rb +102 -76
  207. data/lib/capybara/spec/session/selectors_spec.rb +51 -18
  208. data/lib/capybara/spec/session/sibling_spec.rb +9 -9
  209. data/lib/capybara/spec/session/text_spec.rb +26 -24
  210. data/lib/capybara/spec/session/title_spec.rb +8 -6
  211. data/lib/capybara/spec/session/uncheck_spec.rb +40 -21
  212. data/lib/capybara/spec/session/unselect_spec.rb +37 -37
  213. data/lib/capybara/spec/session/visit_spec.rb +59 -53
  214. data/lib/capybara/spec/session/window/become_closed_spec.rb +22 -19
  215. data/lib/capybara/spec/session/window/current_window_spec.rb +4 -3
  216. data/lib/capybara/spec/session/window/open_new_window_spec.rb +4 -3
  217. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +25 -21
  218. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +10 -5
  219. data/lib/capybara/spec/session/window/window_spec.rb +88 -54
  220. data/lib/capybara/spec/session/window/windows_spec.rb +10 -7
  221. data/lib/capybara/spec/session/window/within_window_spec.rb +17 -16
  222. data/lib/capybara/spec/session/within_spec.rb +69 -44
  223. data/lib/capybara/spec/spec_helper.rb +41 -53
  224. data/lib/capybara/spec/test_app.rb +79 -40
  225. data/lib/capybara/spec/views/animated.erb +49 -0
  226. data/lib/capybara/spec/views/form.erb +134 -42
  227. data/lib/capybara/spec/views/frame_child.erb +4 -3
  228. data/lib/capybara/spec/views/frame_one.erb +2 -1
  229. data/lib/capybara/spec/views/frame_parent.erb +1 -1
  230. data/lib/capybara/spec/views/frame_two.erb +1 -1
  231. data/lib/capybara/spec/views/initial_alert.erb +2 -1
  232. data/lib/capybara/spec/views/layout.erb +10 -0
  233. data/lib/capybara/spec/views/obscured.erb +47 -0
  234. data/lib/capybara/spec/views/offset.erb +33 -0
  235. data/lib/capybara/spec/views/path.erb +2 -2
  236. data/lib/capybara/spec/views/popup_one.erb +1 -1
  237. data/lib/capybara/spec/views/popup_two.erb +1 -1
  238. data/lib/capybara/spec/views/react.erb +45 -0
  239. data/lib/capybara/spec/views/scroll.erb +21 -0
  240. data/lib/capybara/spec/views/spatial.erb +31 -0
  241. data/lib/capybara/spec/views/tables.erb +68 -1
  242. data/lib/capybara/spec/views/with_animation.erb +81 -0
  243. data/lib/capybara/spec/views/with_base_tag.erb +2 -2
  244. data/lib/capybara/spec/views/with_dragula.erb +24 -0
  245. data/lib/capybara/spec/views/with_fixed_header_footer.erb +2 -1
  246. data/lib/capybara/spec/views/with_hover.erb +3 -2
  247. data/lib/capybara/spec/views/with_hover1.erb +10 -0
  248. data/lib/capybara/spec/views/with_html.erb +43 -10
  249. data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
  250. data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
  251. data/lib/capybara/spec/views/with_js.erb +30 -5
  252. data/lib/capybara/spec/views/with_jstree.erb +26 -0
  253. data/lib/capybara/spec/views/with_namespace.erb +21 -0
  254. data/lib/capybara/spec/views/with_scope_other.erb +6 -0
  255. data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
  256. data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
  257. data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
  258. data/lib/capybara/spec/views/with_windows.erb +1 -1
  259. data/lib/capybara/spec/views/within_frames.erb +1 -1
  260. data/lib/capybara/version.rb +1 -1
  261. data/lib/capybara/window.rb +31 -25
  262. data/lib/capybara.rb +120 -100
  263. data/spec/basic_node_spec.rb +59 -34
  264. data/spec/capybara_spec.rb +54 -52
  265. data/spec/css_builder_spec.rb +101 -0
  266. data/spec/css_splitter_spec.rb +38 -0
  267. data/spec/dsl_spec.rb +80 -53
  268. data/spec/filter_set_spec.rb +24 -7
  269. data/spec/fixtures/certificate.pem +25 -0
  270. data/spec/fixtures/key.pem +27 -0
  271. data/spec/fixtures/selenium_driver_rspec_failure.rb +4 -4
  272. data/spec/fixtures/selenium_driver_rspec_success.rb +4 -4
  273. data/spec/minitest_spec.rb +38 -5
  274. data/spec/minitest_spec_spec.rb +88 -62
  275. data/spec/per_session_config_spec.rb +5 -5
  276. data/spec/rack_test_spec.rb +169 -115
  277. data/spec/regexp_dissassembler_spec.rb +250 -0
  278. data/spec/result_spec.rb +86 -33
  279. data/spec/rspec/features_spec.rb +26 -23
  280. data/spec/rspec/scenarios_spec.rb +8 -4
  281. data/spec/rspec/shared_spec_matchers.rb +393 -363
  282. data/spec/rspec/views_spec.rb +4 -3
  283. data/spec/rspec_matchers_spec.rb +10 -10
  284. data/spec/rspec_spec.rb +109 -85
  285. data/spec/sauce_spec_chrome.rb +43 -0
  286. data/spec/selector_spec.rb +391 -61
  287. data/spec/selenium_spec_chrome.rb +180 -41
  288. data/spec/selenium_spec_chrome_remote.rb +101 -0
  289. data/spec/selenium_spec_edge.rb +28 -8
  290. data/spec/selenium_spec_firefox.rb +211 -0
  291. data/spec/selenium_spec_firefox_remote.rb +80 -0
  292. data/spec/selenium_spec_ie.rb +127 -11
  293. data/spec/selenium_spec_safari.rb +156 -0
  294. data/spec/server_spec.rb +177 -78
  295. data/spec/session_spec.rb +52 -16
  296. data/spec/shared_selenium_node.rb +79 -0
  297. data/spec/shared_selenium_session.rb +455 -123
  298. data/spec/spec_helper.rb +91 -2
  299. data/spec/xpath_builder_spec.rb +93 -0
  300. metadata +343 -42
  301. data/.yard/templates_custom/default/class/html/selectors.erb +0 -38
  302. data/.yard/templates_custom/default/class/html/setup.rb +0 -17
  303. data/.yard/yard_extensions.rb +0 -78
  304. data/lib/capybara/rspec/compound.rb +0 -90
  305. data/lib/capybara/spec/session/assert_selector.rb +0 -149
  306. data/lib/capybara/spec/session/source_spec.rb +0 -0
  307. data/lib/capybara/spec/views/with_title.erb +0 -5
  308. data/spec/selenium_spec_marionette.rb +0 -143
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'capybara/rack_test/errors'
4
+
3
5
  class Capybara::RackTest::Node < Capybara::Driver::Node
4
6
  BLOCK_ELEMENTS = %w[p h1 h2 h3 h4 h5 h6 ol ul pre address blockquote dl div fieldset form hr noscript table].freeze
5
7
 
@@ -7,16 +9,16 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
7
9
  native.text
8
10
  .gsub(/[\u200b\u200e\u200f]/, '')
9
11
  .gsub(/[\ \n\f\t\v\u2028\u2029]+/, ' ')
10
- .gsub(/\A[[:space:]&&[^\u00a0]]+/, "")
11
- .gsub(/[[:space:]&&[^\u00a0]]+\z/, "")
12
+ .gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
13
+ .gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
12
14
  .tr("\u00a0", ' ')
13
15
  end
14
16
 
15
17
  def visible_text
16
- displayed_text.gsub(/\ +/, ' ')
18
+ displayed_text.squeeze(' ')
17
19
  .gsub(/[\ \n]*\n[\ \n]*/, "\n")
18
- .gsub(/\A[[:space:]&&[^\u00a0]]+/, "")
19
- .gsub(/[[:space:]&&[^\u00a0]]+\z/, "")
20
+ .gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
21
+ .gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
20
22
  .tr("\u00a0", ' ')
21
23
  end
22
24
 
@@ -24,6 +26,10 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
24
26
  string_node[name]
25
27
  end
26
28
 
29
+ def style(_styles)
30
+ raise NotImplementedError, 'The rack_test driver does not process CSS'
31
+ end
32
+
27
33
  def value
28
34
  string_node.value
29
35
  end
@@ -39,6 +45,7 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
39
45
 
40
46
  if radio? then set_radio(value)
41
47
  elsif checkbox? then set_checkbox(value)
48
+ elsif range? then set_range(value)
42
49
  elsif input_field? then set_input(value)
43
50
  elsif textarea? then native['_capybara_raw_value'] = value.to_s
44
51
  end
@@ -46,17 +53,20 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
46
53
 
47
54
  def select_option
48
55
  return if disabled?
56
+
49
57
  deselect_options unless select_node.multiple?
50
- native["selected"] = 'selected'
58
+ native['selected'] = 'selected'
51
59
  end
52
60
 
53
61
  def unselect_option
54
- raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box." unless select_node.multiple?
62
+ raise Capybara::UnselectNotAllowed, 'Cannot unselect option from single select box.' unless select_node.multiple?
63
+
55
64
  native.remove_attribute('selected')
56
65
  end
57
66
 
58
- def click(keys = [], offset = {})
59
- raise ArgumentError, "The RackTest driver does not support click options" unless keys.empty? && offset.empty?
67
+ def click(keys = [], **options)
68
+ options.delete(:offset)
69
+ raise ArgumentError, 'The RackTest driver does not support click options' unless keys.empty? && options.empty?
60
70
 
61
71
  if link?
62
72
  follow_link
@@ -67,6 +77,8 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
67
77
  set(!checked?)
68
78
  elsif tag_name == 'label'
69
79
  click_label
80
+ elsif (details = native.xpath('.//ancestor-or-self::details').last)
81
+ toggle_details(details)
70
82
  end
71
83
  end
72
84
 
@@ -90,26 +102,48 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
90
102
  return true if string_node.disabled?
91
103
 
92
104
  if %w[option optgroup].include? tag_name
93
- find_xpath("parent::*[self::optgroup or self::select]")[0].disabled?
105
+ find_xpath(OPTION_OWNER_XPATH)[0].disabled?
94
106
  else
95
- !find_xpath("parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]").empty?
107
+ !find_xpath(DISABLED_BY_FIELDSET_XPATH).empty?
96
108
  end
97
109
  end
98
110
 
111
+ def readonly?
112
+ # readonly attribute not valid on these input types
113
+ return false if input_field? && %w[hidden range color checkbox radio file submit image reset button].include?(type)
114
+
115
+ super
116
+ end
117
+
99
118
  def path
100
119
  native.path
101
120
  end
102
121
 
103
- def find_xpath(locator)
104
- native.xpath(locator).map { |n| self.class.new(driver, n) }
122
+ def find_xpath(locator, **_hints)
123
+ native.xpath(locator).map { |el| self.class.new(driver, el) }
105
124
  end
106
125
 
107
- def find_css(locator)
108
- native.css(locator, Capybara::RackTest::CSSHandlers.new).map { |n| self.class.new(driver, n) }
126
+ def find_css(locator, **_hints)
127
+ native.css(locator, Capybara::RackTest::CSSHandlers.new).map { |el| self.class.new(driver, el) }
109
128
  end
110
129
 
111
- def ==(other)
112
- native == other.native
130
+ public_instance_methods(false).each do |meth_name|
131
+ alias_method "unchecked_#{meth_name}", meth_name
132
+ private "unchecked_#{meth_name}" # rubocop:disable Style/AccessModifierDeclarations
133
+
134
+ if RUBY_VERSION >= '2.7'
135
+ class_eval <<~METHOD, __FILE__, __LINE__ + 1
136
+ def #{meth_name}(...)
137
+ stale_check
138
+ method(:"unchecked_#{meth_name}").call(...)
139
+ end
140
+ METHOD
141
+ else
142
+ define_method meth_name do |*args|
143
+ stale_check
144
+ send("unchecked_#{meth_name}", *args)
145
+ end
146
+ end
113
147
  end
114
148
 
115
149
  protected
@@ -128,15 +162,19 @@ protected
128
162
  end.join || ''
129
163
  text = "\n#{text}\n" if BLOCK_ELEMENTS.include?(tag_name)
130
164
  text
131
- else
165
+ else # rubocop:disable Lint/DuplicateBranch
132
166
  ''
133
167
  end
134
168
  end
135
169
 
136
170
  private
137
171
 
172
+ def stale_check
173
+ raise Capybara::RackTest::Errors::StaleElementReferenceError unless native.document == driver.dom
174
+ end
175
+
138
176
  def deselect_options
139
- select_node.find_xpath(".//option[@selected]").each { |node| node.native.remove_attribute("selected") }
177
+ select_node.find_xpath('.//option[@selected]').each { |node| node.native.remove_attribute('selected') }
140
178
  end
141
179
 
142
180
  def string_node
@@ -161,8 +199,8 @@ private
161
199
  end
162
200
 
163
201
  def set_radio(_value) # rubocop:disable Naming/AccessorMethodName
164
- other_radios_xpath = XPath.generate { |x| x.anywhere(:input)[x.attr(:name) == self[:name]] }.to_s
165
- driver.dom.xpath(other_radios_xpath).each { |node| node.remove_attribute("checked") }
202
+ other_radios_xpath = XPath.generate { |xp| xp.anywhere(:input)[xp.attr(:name) == self[:name]] }.to_s
203
+ driver.dom.xpath(other_radios_xpath).each { |node| node.remove_attribute('checked') }
166
204
  native['checked'] = 'checked'
167
205
  end
168
206
 
@@ -174,6 +212,14 @@ private
174
212
  end
175
213
  end
176
214
 
215
+ def set_range(value) # rubocop:disable Naming/AccessorMethodName
216
+ min, max, step = (native['min'] || 0).to_f, (native['max'] || 100).to_f, (native['step'] || 1).to_f
217
+ value = value.to_f
218
+ value = value.clamp(min, max)
219
+ value = (((value - min) / step).round * step) + min
220
+ native['value'] = value.clamp(min, max)
221
+ end
222
+
177
223
  def set_input(value) # rubocop:disable Naming/AccessorMethodName
178
224
  if text_or_password? && attribute_is_not_blank?(:maxlength)
179
225
  # Browser behavior for maxlength="0" is inconsistent, so we stick with
@@ -181,11 +227,11 @@ private
181
227
  value = value.to_s[0...self[:maxlength].to_i]
182
228
  end
183
229
  if value.is_a?(Array) # Assert multiple attribute is present
184
- value.each do |v|
230
+ value.each do |val|
185
231
  new_native = native.clone
186
232
  new_native.remove_attribute('value')
187
233
  native.add_next_sibling(new_native)
188
- new_native['value'] = v.to_s
234
+ new_native['value'] = val.to_s
189
235
  end
190
236
  native.remove
191
237
  else
@@ -198,7 +244,7 @@ private
198
244
  end
199
245
 
200
246
  def follow_link
201
- method = self["data-method"] if driver.options[:respect_data_method]
247
+ method = self['data-method'] if driver.options[:respect_data_method]
202
248
  method ||= :get
203
249
  driver.follow(method, self[:href].to_s)
204
250
  end
@@ -207,11 +253,20 @@ private
207
253
  labelled_control = if native[:for]
208
254
  find_xpath("//input[@id='#{native[:for]}']")
209
255
  else
210
- find_xpath(".//input")
256
+ find_xpath('.//input')
211
257
  end.first
212
258
 
213
- if labelled_control && (labelled_control.checkbox? || labelled_control.radio?)
214
- labelled_control.set(!labelled_control.checked?)
259
+ labelled_control.set(!labelled_control.checked?) if checkbox_or_radio?(labelled_control)
260
+ end
261
+
262
+ def toggle_details(details = nil)
263
+ details ||= native.xpath('.//ancestor-or-self::details').last
264
+ return unless details
265
+
266
+ if details.has_attribute?('open')
267
+ details.remove_attribute('open')
268
+ else
269
+ details.set_attribute('open', 'open')
215
270
  end
216
271
  end
217
272
 
@@ -220,32 +275,54 @@ private
220
275
  end
221
276
 
222
277
  def submits?
223
- (tag_name == 'input' and %w[submit image].include?(type)) || (tag_name == 'button' and [nil, "submit"].include?(type))
278
+ (tag_name == 'input' && %w[submit image].include?(type)) || (tag_name == 'button' && [nil, 'submit'].include?(type))
224
279
  end
225
280
 
226
281
  def checkable?
227
- tag_name == 'input' and %w[checkbox radio].include?(type)
282
+ tag_name == 'input' && %w[checkbox radio].include?(type)
228
283
  end
229
284
 
230
285
  protected
231
286
 
232
- def checkbox?
233
- input_field? && type == 'checkbox'
287
+ def checkbox_or_radio?(field = self)
288
+ field&.checkbox? || field&.radio?
234
289
  end
235
290
 
236
- def input_field?
237
- tag_name == 'input'
291
+ def checkbox?
292
+ input_field? && type == 'checkbox'
238
293
  end
239
294
 
240
295
  def radio?
241
296
  input_field? && type == 'radio'
242
297
  end
243
298
 
244
- def textarea?
245
- tag_name == "textarea"
246
- end
247
-
248
299
  def text_or_password?
249
300
  input_field? && (type == 'text' || type == 'password')
250
301
  end
302
+
303
+ def input_field?
304
+ tag_name == 'input'
305
+ end
306
+
307
+ def textarea?
308
+ tag_name == 'textarea'
309
+ end
310
+
311
+ def range?
312
+ input_field? && type == 'range'
313
+ end
314
+
315
+ OPTION_OWNER_XPATH = XPath.parent(:optgroup, :select, :datalist).to_s.freeze
316
+ DISABLED_BY_FIELDSET_XPATH = XPath.generate do |x|
317
+ x.parent(:fieldset)[
318
+ XPath.attr(:disabled)
319
+ ] + x.ancestor[
320
+ ~x.self(:legend) |
321
+ x.preceding_sibling(:legend)
322
+ ][
323
+ x.parent(:fieldset)[
324
+ x.attr(:disabled)
325
+ ]
326
+ ]
327
+ end.to_s.freeze
251
328
  end
@@ -3,7 +3,7 @@
3
3
  require 'capybara/dsl'
4
4
 
5
5
  Capybara.app = Rack::Builder.new do
6
- map "/" do
6
+ map '/' do
7
7
  run Rails.application
8
8
  end
9
9
  end.to_app
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capybara
4
+ # @api private
5
+ class RegistrationContainer
6
+ def names
7
+ @registered.keys
8
+ end
9
+
10
+ def [](name)
11
+ @registered[name]
12
+ end
13
+
14
+ def []=(name, value)
15
+ Capybara::Helpers.warn 'DEPRECATED: Directly setting drivers/servers is deprecated, please use Capybara.register_driver/register_server instead'
16
+ @registered[name] = value
17
+ end
18
+
19
+ def method_missing(method_name, *args, **options, &block)
20
+ if @registered.respond_to?(method_name)
21
+ Capybara::Helpers.warn "DEPRECATED: Calling '#{method_name}' on the drivers/servers container is deprecated without replacement"
22
+ # RUBY 2.6 will send an empty hash rather than nothing with **options so fix that
23
+ return @registered.public_send(method_name, *args, &block) if options.empty?
24
+
25
+ return @registered.public_send(method_name, *args, **options, &block)
26
+ end
27
+ super
28
+ end
29
+
30
+ def respond_to_missing?(method_name, include_all)
31
+ @registered.respond_to?(method_name) || super
32
+ end
33
+
34
+ private
35
+
36
+ def initialize
37
+ @registered = {}
38
+ end
39
+
40
+ def register(name, block)
41
+ @registered[name] = block
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ Capybara.register_driver :rack_test do |app|
4
+ Capybara::RackTest::Driver.new(app)
5
+ end
6
+
7
+ Capybara.register_driver :selenium do |app|
8
+ Capybara::Selenium::Driver.new(app)
9
+ end
10
+
11
+ Capybara.register_driver :selenium_headless do |app|
12
+ version = Capybara::Selenium::Driver.load_selenium
13
+ options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
14
+ browser_options = ::Selenium::WebDriver::Firefox::Options.new.tap do |opts|
15
+ opts.add_argument '-headless'
16
+ end
17
+ Capybara::Selenium::Driver.new(app, **{ :browser => :firefox, options_key => browser_options })
18
+ end
19
+
20
+ Capybara.register_driver :selenium_chrome do |app|
21
+ version = Capybara::Selenium::Driver.load_selenium
22
+ options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
23
+ browser_options = ::Selenium::WebDriver::Chrome::Options.new.tap do |opts|
24
+ # Workaround https://bugs.chromium.org/p/chromedriver/issues/detail?id=2650&q=load&sort=-id&colspec=ID%20Status%20Pri%20Owner%20Summary
25
+ opts.add_argument('--disable-site-isolation-trials')
26
+ end
27
+
28
+ Capybara::Selenium::Driver.new(app, **{ :browser => :chrome, options_key => browser_options })
29
+ end
30
+
31
+ Capybara.register_driver :selenium_chrome_headless do |app|
32
+ version = Capybara::Selenium::Driver.load_selenium
33
+ options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
34
+ browser_options = ::Selenium::WebDriver::Chrome::Options.new.tap do |opts|
35
+ opts.add_argument('--headless')
36
+ opts.add_argument('--disable-gpu') if Gem.win_platform?
37
+ # Workaround https://bugs.chromium.org/p/chromedriver/issues/detail?id=2650&q=load&sort=-id&colspec=ID%20Status%20Pri%20Owner%20Summary
38
+ opts.add_argument('--disable-site-isolation-trials')
39
+ end
40
+
41
+ Capybara::Selenium::Driver.new(app, **{ :browser => :chrome, options_key => browser_options })
42
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Puma
4
+ module MiniSSL
5
+ class Socket
6
+ def read_nonblock(size, *_)
7
+ wait_states = %i[wait_readable wait_writable]
8
+
9
+ loop do
10
+ output = engine_read_all
11
+ return output if output
12
+
13
+ data = @socket.read_nonblock(size, exception: false)
14
+ raise IO::EAGAINWaitReadable if wait_states.include? data
15
+ return nil if data.nil?
16
+
17
+ @engine.inject(data)
18
+ output = engine_read_all
19
+
20
+ return output if output
21
+
22
+ while (neg_data = @engine.extract)
23
+ @socket.write neg_data
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ Capybara.register_server :default do |app, port, _host|
4
+ Capybara.run_default_server(app, port)
5
+ end
6
+
7
+ Capybara.register_server :webrick do |app, port, host, **options|
8
+ require 'rack/handler/webrick'
9
+ options = { Host: host, Port: port, AccessLog: [], Logger: WEBrick::Log.new(nil, 0) }.merge(options)
10
+ Rack::Handler::WEBrick.run(app, **options)
11
+ end
12
+
13
+ Capybara.register_server :puma do |app, port, host, **options|
14
+ begin
15
+ require 'rack/handler/puma'
16
+ rescue LoadError
17
+ raise LoadError, 'Capybara is unable to load `puma` for its server, please add `puma` to your project or specify a different server via something like `Capybara.server = :webrick`.'
18
+ else
19
+ unless Rack::Handler::Puma.respond_to?(:config)
20
+ raise LoadError, 'Capybara requires `puma` version 3.8.0 or higher, please upgrade `puma` or register and specify your own server block'
21
+ end
22
+ end
23
+
24
+ # If we just run the Puma Rack handler it installs signal handlers which prevent us from being able to interrupt tests.
25
+ # Therefore construct and run the Server instance ourselves.
26
+ # Rack::Handler::Puma.run(app, { Host: host, Port: port, Threads: "0:4", workers: 0, daemon: false }.merge(options))
27
+ default_options = { Host: host, Port: port, Threads: '0:4', workers: 0, daemon: false }
28
+ options = default_options.merge(options)
29
+
30
+ conf = Rack::Handler::Puma.config(app, options)
31
+ conf.clamp
32
+ events = conf.options[:Silent] ? ::Puma::Events.strings : ::Puma::Events.stdio
33
+
34
+ puma_ver = Gem::Version.new(Puma::Const::PUMA_VERSION)
35
+ require_relative 'patches/puma_ssl' if Gem::Requirement.new('>=4.0.0', '< 4.1.0').satisfied_by?(puma_ver)
36
+
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]
44
+ end.run.join
45
+ end
@@ -7,7 +7,7 @@ module Capybara
7
7
  # A {Capybara::Result} represents a collection of {Capybara::Node::Element} on the page. It is possible to interact with this
8
8
  # collection similar to an Array because it implements Enumerable and offers the following Array methods through delegation:
9
9
  #
10
- # * []
10
+ # * \[\]
11
11
  # * each()
12
12
  # * at()
13
13
  # * size()
@@ -16,6 +16,8 @@ module Capybara
16
16
  # * first()
17
17
  # * last()
18
18
  # * empty?()
19
+ # * values_at()
20
+ # * sample()
19
21
  #
20
22
  # @see Capybara::Node::Element
21
23
  #
@@ -26,35 +28,52 @@ module Capybara
26
28
  def initialize(elements, query)
27
29
  @elements = elements
28
30
  @result_cache = []
29
- @results_enum = lazy_select_elements { |node| query.matches_filters?(node) }
31
+ @filter_errors = []
32
+ @results_enum = lazy_select_elements { |node| query.matches_filters?(node, @filter_errors) }
30
33
  @query = query
34
+ @allow_reload = false
31
35
  end
32
36
 
33
37
  def_delegators :full_results, :size, :length, :last, :values_at, :inspect, :sample
34
38
 
35
- alias :index :find_index
39
+ alias index find_index
36
40
 
37
41
  def each(&block)
38
- return enum_for(:each) unless block_given?
42
+ return enum_for(:each) unless block
39
43
 
40
44
  @result_cache.each(&block)
41
45
  loop do
42
46
  next_result = @results_enum.next
43
- @result_cache << next_result
47
+ add_to_cache(next_result)
44
48
  yield next_result
45
49
  end
46
50
  self
47
51
  end
48
52
 
49
53
  def [](*args)
50
- if (args.size == 1) && ((idx = args[0]).is_a? Integer) && (idx >= 0)
51
- @result_cache << @results_enum.next while @result_cache.size <= idx
52
- @result_cache[idx]
53
- else
54
+ idx, length = args
55
+ max_idx = case idx
56
+ when Integer
57
+ if idx.negative?
58
+ nil
59
+ else
60
+ length.nil? ? idx : idx + length - 1
61
+ end
62
+ when Range
63
+ # idx.max is broken with beginless ranges
64
+ # idx.end && idx.max # endless range will have end == nil
65
+ max = idx.end
66
+ max = nil if max&.negative?
67
+ max -= 1 if max && idx.exclude_end?
68
+ max
69
+ end
70
+
71
+ if max_idx.nil?
54
72
  full_results[*args]
73
+ else
74
+ load_up_to(max_idx + 1)
75
+ @result_cache[*args]
55
76
  end
56
- rescue StopIteration
57
- return nil
58
77
  end
59
78
  alias :at :[]
60
79
 
@@ -63,46 +82,28 @@ module Capybara
63
82
  end
64
83
 
65
84
  def compare_count
85
+ return 0 unless @query
86
+
87
+ count, min, max, between = @query.options.values_at(:count, :minimum, :maximum, :between)
88
+
66
89
  # Only check filters for as many elements as necessary to determine result
67
- if @query.options[:count]
68
- count_opt = Integer(@query.options[:count])
69
- loop do
70
- break if @result_cache.size > count_opt
71
- @result_cache << @results_enum.next
72
- end
73
- return @result_cache.size <=> count_opt
90
+ if count && (count = Integer(count))
91
+ return load_up_to(count + 1) <=> count
74
92
  end
75
93
 
76
- if @query.options[:minimum]
77
- min_opt = Integer(@query.options[:minimum])
78
- begin
79
- @result_cache << @results_enum.next while @result_cache.size < min_opt
80
- rescue StopIteration
81
- return -1
82
- end
83
- end
94
+ return -1 if min && (min = Integer(min)) && (load_up_to(min) < min)
84
95
 
85
- if @query.options[:maximum]
86
- max_opt = Integer(@query.options[:maximum])
87
- begin
88
- @result_cache << @results_enum.next while @result_cache.size <= max_opt
89
- return 1
90
- rescue StopIteration
91
- end
92
- end
96
+ return 1 if max && (max = Integer(max)) && (load_up_to(max + 1) > max)
93
97
 
94
- if @query.options[:between]
95
- min, max = @query.options[:between].minmax
96
- loop do
97
- break if @result_cache.size > max
98
- @result_cache << @results_enum.next
99
- end
100
- return 0 if @query.options[:between].include?(@result_cache.size)
101
- return -1 if @result_cache.size < min
102
- return 1
98
+ if between
99
+ min, max = (between.begin && between.min) || 1, between.end
100
+ max -= 1 if max && between.exclude_end?
101
+
102
+ size = load_up_to(max ? max + 1 : min)
103
+ return size <=> min unless between.include?(size)
103
104
  end
104
105
 
105
- return 0
106
+ 0
106
107
  end
107
108
 
108
109
  def matches_count?
@@ -112,13 +113,15 @@ module Capybara
112
113
  def failure_message
113
114
  message = @query.failure_message
114
115
  if count.zero?
115
- message << " but there were no matches"
116
+ message << ' but there were no matches'
116
117
  else
117
- message << ", found #{count} #{Capybara::Helpers.declension("match", "matches", count)}: " << full_results.map(&:text).map(&:inspect).join(", ")
118
+ message << ", found #{count} #{Capybara::Helpers.declension('match', 'matches', count)}: " \
119
+ << full_results.map(&:text).map(&:inspect).join(', ')
118
120
  end
119
121
  unless rest.empty?
120
- elements = rest.map { |el| el.text rescue "<<ERROR>>" }.map(&:inspect).join(", ")
121
- message << ". Also found " << elements << ", which matched the selector but not all filters."
122
+ elements = rest.map { |el| el.text rescue '<<ERROR>>' }.map(&:inspect).join(', ') # rubocop:disable Style/RescueModifier
123
+ message << '. Also found ' << elements << ', which matched the selector but not all filters. '
124
+ message << @filter_errors.join('. ') if (rest.size == 1) && count.zero?
122
125
  end
123
126
  message
124
127
  end
@@ -127,8 +130,34 @@ module Capybara
127
130
  failure_message.sub(/(to find)/, 'not \1')
128
131
  end
129
132
 
133
+ def unfiltered_size
134
+ @elements.length
135
+ end
136
+
137
+ ##
138
+ # @api private
139
+ #
140
+ def allow_reload!
141
+ @allow_reload = true
142
+ self
143
+ end
144
+
130
145
  private
131
146
 
147
+ def add_to_cache(elem)
148
+ elem.allow_reload!(@result_cache.size) if @allow_reload
149
+ @result_cache << elem
150
+ end
151
+
152
+ def load_up_to(num)
153
+ loop do
154
+ break if @result_cache.size >= num
155
+
156
+ add_to_cache(@results_enum.next)
157
+ end
158
+ @result_cache.size
159
+ end
160
+
132
161
  def full_results
133
162
  loop { @result_cache << @results_enum.next }
134
163
  @result_cache
@@ -138,13 +167,18 @@ module Capybara
138
167
  @rest ||= @elements - full_results
139
168
  end
140
169
 
141
- def lazy_select_elements(&block)
142
- # JRuby has an issue with lazy enumerators which
170
+ if RUBY_PLATFORM == 'java'
171
+ # JRuby < 9.2.8.0 has an issue with lazy enumerators which
143
172
  # causes a concurrency issue with network requests here
144
173
  # https://github.com/jruby/jruby/issues/4212
145
- if RUBY_PLATFORM == 'java'
174
+ # while JRuby >= 9.2.8.0 leaks threads when using lazy enumerators
175
+ # https://github.com/teamcapybara/capybara/issues/2349
176
+ # so disable the use and JRuby users will need to pay a performance penalty
177
+ def lazy_select_elements(&block)
146
178
  @elements.select(&block).to_enum # non-lazy evaluation
147
- else
179
+ end
180
+ else
181
+ def lazy_select_elements(&block)
148
182
  @elements.lazy.select(&block)
149
183
  end
150
184
  end