capybara 2.18.0 → 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 (316) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/History.md +945 -12
  4. data/License.txt +1 -1
  5. data/README.md +264 -90
  6. data/lib/capybara/config.rb +29 -57
  7. data/lib/capybara/cucumber.rb +2 -3
  8. data/lib/capybara/driver/base.rb +35 -18
  9. data/lib/capybara/driver/node.rb +40 -10
  10. data/lib/capybara/dsl.rb +10 -7
  11. data/lib/capybara/helpers.rb +70 -31
  12. data/lib/capybara/minitest/spec.rb +185 -83
  13. data/lib/capybara/minitest.rb +232 -112
  14. data/lib/capybara/node/actions.rb +274 -171
  15. data/lib/capybara/node/base.rb +42 -34
  16. data/lib/capybara/node/document.rb +15 -3
  17. data/lib/capybara/node/document_matchers.rb +19 -21
  18. data/lib/capybara/node/element.rb +362 -135
  19. data/lib/capybara/node/finders.rb +151 -137
  20. data/lib/capybara/node/matchers.rb +394 -209
  21. data/lib/capybara/node/simple.rb +59 -26
  22. data/lib/capybara/node/whitespace_normalizer.rb +81 -0
  23. data/lib/capybara/queries/active_element_query.rb +18 -0
  24. data/lib/capybara/queries/ancestor_query.rb +12 -9
  25. data/lib/capybara/queries/base_query.rb +39 -28
  26. data/lib/capybara/queries/current_path_query.rb +21 -27
  27. data/lib/capybara/queries/match_query.rb +14 -7
  28. data/lib/capybara/queries/selector_query.rb +659 -149
  29. data/lib/capybara/queries/sibling_query.rb +11 -9
  30. data/lib/capybara/queries/style_query.rb +45 -0
  31. data/lib/capybara/queries/text_query.rb +56 -38
  32. data/lib/capybara/queries/title_query.rb +8 -11
  33. data/lib/capybara/rack_test/browser.rb +114 -42
  34. data/lib/capybara/rack_test/css_handlers.rb +6 -4
  35. data/lib/capybara/rack_test/driver.rb +22 -17
  36. data/lib/capybara/rack_test/errors.rb +6 -0
  37. data/lib/capybara/rack_test/form.rb +93 -58
  38. data/lib/capybara/rack_test/node.rb +184 -81
  39. data/lib/capybara/rails.rb +3 -7
  40. data/lib/capybara/registration_container.rb +41 -0
  41. data/lib/capybara/registrations/drivers.rb +42 -0
  42. data/lib/capybara/registrations/patches/puma_ssl.rb +29 -0
  43. data/lib/capybara/registrations/servers.rb +66 -0
  44. data/lib/capybara/result.rb +97 -63
  45. data/lib/capybara/rspec/features.rb +17 -50
  46. data/lib/capybara/rspec/matcher_proxies.rb +52 -15
  47. data/lib/capybara/rspec/matchers/base.rb +113 -0
  48. data/lib/capybara/rspec/matchers/become_closed.rb +33 -0
  49. data/lib/capybara/rspec/matchers/compound.rb +88 -0
  50. data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
  51. data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
  52. data/lib/capybara/rspec/matchers/have_current_path.rb +29 -0
  53. data/lib/capybara/rspec/matchers/have_selector.rb +69 -0
  54. data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
  55. data/lib/capybara/rspec/matchers/have_text.rb +33 -0
  56. data/lib/capybara/rspec/matchers/have_title.rb +29 -0
  57. data/lib/capybara/rspec/matchers/match_selector.rb +27 -0
  58. data/lib/capybara/rspec/matchers/match_style.rb +43 -0
  59. data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
  60. data/lib/capybara/rspec/matchers.rb +146 -310
  61. data/lib/capybara/rspec.rb +7 -11
  62. data/lib/capybara/selector/builders/css_builder.rb +84 -0
  63. data/lib/capybara/selector/builders/xpath_builder.rb +71 -0
  64. data/lib/capybara/selector/css.rb +85 -13
  65. data/lib/capybara/selector/definition/button.rb +68 -0
  66. data/lib/capybara/selector/definition/checkbox.rb +26 -0
  67. data/lib/capybara/selector/definition/css.rb +10 -0
  68. data/lib/capybara/selector/definition/datalist_input.rb +35 -0
  69. data/lib/capybara/selector/definition/datalist_option.rb +25 -0
  70. data/lib/capybara/selector/definition/element.rb +28 -0
  71. data/lib/capybara/selector/definition/field.rb +40 -0
  72. data/lib/capybara/selector/definition/fieldset.rb +14 -0
  73. data/lib/capybara/selector/definition/file_field.rb +13 -0
  74. data/lib/capybara/selector/definition/fillable_field.rb +33 -0
  75. data/lib/capybara/selector/definition/frame.rb +17 -0
  76. data/lib/capybara/selector/definition/id.rb +6 -0
  77. data/lib/capybara/selector/definition/label.rb +62 -0
  78. data/lib/capybara/selector/definition/link.rb +55 -0
  79. data/lib/capybara/selector/definition/link_or_button.rb +16 -0
  80. data/lib/capybara/selector/definition/option.rb +27 -0
  81. data/lib/capybara/selector/definition/radio_button.rb +27 -0
  82. data/lib/capybara/selector/definition/select.rb +81 -0
  83. data/lib/capybara/selector/definition/table.rb +109 -0
  84. data/lib/capybara/selector/definition/table_row.rb +21 -0
  85. data/lib/capybara/selector/definition/xpath.rb +5 -0
  86. data/lib/capybara/selector/definition.rb +280 -0
  87. data/lib/capybara/selector/filter.rb +2 -17
  88. data/lib/capybara/selector/filter_set.rb +80 -33
  89. data/lib/capybara/selector/filters/base.rb +50 -6
  90. data/lib/capybara/selector/filters/expression_filter.rb +8 -26
  91. data/lib/capybara/selector/filters/locator_filter.rb +29 -0
  92. data/lib/capybara/selector/filters/node_filter.rb +16 -12
  93. data/lib/capybara/selector/regexp_disassembler.rb +211 -0
  94. data/lib/capybara/selector/selector.rb +93 -210
  95. data/lib/capybara/selector/xpath_extensions.rb +17 -0
  96. data/lib/capybara/selector.rb +475 -523
  97. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -0
  98. data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -0
  99. data/lib/capybara/selenium/atoms/src/getAttribute.js +161 -0
  100. data/lib/capybara/selenium/atoms/src/isDisplayed.js +454 -0
  101. data/lib/capybara/selenium/driver.rb +298 -267
  102. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +117 -0
  103. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +128 -0
  104. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +84 -0
  105. data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +26 -0
  106. data/lib/capybara/selenium/driver_specializations/safari_driver.rb +24 -0
  107. data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
  108. data/lib/capybara/selenium/extensions/find.rb +110 -0
  109. data/lib/capybara/selenium/extensions/html5_drag.rb +229 -0
  110. data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
  111. data/lib/capybara/selenium/extensions/scroll.rb +76 -0
  112. data/lib/capybara/selenium/node.rb +517 -145
  113. data/lib/capybara/selenium/nodes/chrome_node.rb +125 -0
  114. data/lib/capybara/selenium/nodes/edge_node.rb +110 -0
  115. data/lib/capybara/selenium/nodes/firefox_node.rb +136 -0
  116. data/lib/capybara/selenium/nodes/ie_node.rb +22 -0
  117. data/lib/capybara/selenium/nodes/safari_node.rb +118 -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 +80 -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 +59 -67
  127. data/lib/capybara/session/config.rb +81 -67
  128. data/lib/capybara/session/matchers.rb +28 -20
  129. data/lib/capybara/session.rb +337 -365
  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 +151 -12
  133. data/lib/capybara/spec/session/accept_alert_spec.rb +12 -11
  134. data/lib/capybara/spec/session/accept_confirm_spec.rb +6 -5
  135. data/lib/capybara/spec/session/accept_prompt_spec.rb +10 -10
  136. data/lib/capybara/spec/session/active_element_spec.rb +31 -0
  137. data/lib/capybara/spec/session/all_spec.rb +161 -57
  138. data/lib/capybara/spec/session/ancestor_spec.rb +27 -24
  139. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +68 -38
  140. data/lib/capybara/spec/session/assert_current_path_spec.rb +75 -0
  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_spec.rb +258 -0
  144. data/lib/capybara/spec/session/{assert_title.rb → assert_title_spec.rb} +22 -12
  145. data/lib/capybara/spec/session/attach_file_spec.rb +144 -69
  146. data/lib/capybara/spec/session/body_spec.rb +12 -13
  147. data/lib/capybara/spec/session/check_spec.rb +117 -55
  148. data/lib/capybara/spec/session/choose_spec.rb +64 -31
  149. data/lib/capybara/spec/session/click_button_spec.rb +231 -173
  150. data/lib/capybara/spec/session/click_link_or_button_spec.rb +55 -35
  151. data/lib/capybara/spec/session/click_link_spec.rb +93 -58
  152. data/lib/capybara/spec/session/current_scope_spec.rb +12 -11
  153. data/lib/capybara/spec/session/current_url_spec.rb +57 -39
  154. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +4 -4
  155. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +3 -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 +9 -7
  159. data/lib/capybara/spec/session/element/matches_selector_spec.rb +71 -57
  160. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +8 -7
  161. data/lib/capybara/spec/session/evaluate_script_spec.rb +29 -8
  162. data/lib/capybara/spec/session/execute_script_spec.rb +10 -8
  163. data/lib/capybara/spec/session/fill_in_spec.rb +134 -43
  164. data/lib/capybara/spec/session/find_button_spec.rb +25 -24
  165. data/lib/capybara/spec/session/find_by_id_spec.rb +10 -9
  166. data/lib/capybara/spec/session/find_field_spec.rb +37 -41
  167. data/lib/capybara/spec/session/find_link_spec.rb +46 -17
  168. data/lib/capybara/spec/session/find_spec.rb +260 -145
  169. data/lib/capybara/spec/session/first_spec.rb +80 -52
  170. data/lib/capybara/spec/session/frame/frame_title_spec.rb +23 -0
  171. data/lib/capybara/spec/session/frame/frame_url_spec.rb +23 -0
  172. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +33 -20
  173. data/lib/capybara/spec/session/frame/within_frame_spec.rb +52 -32
  174. data/lib/capybara/spec/session/go_back_spec.rb +2 -1
  175. data/lib/capybara/spec/session/go_forward_spec.rb +2 -1
  176. data/lib/capybara/spec/session/has_all_selectors_spec.rb +31 -31
  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 +100 -13
  180. data/lib/capybara/spec/session/has_css_spec.rb +272 -137
  181. data/lib/capybara/spec/session/has_current_path_spec.rb +60 -61
  182. data/lib/capybara/spec/session/has_element_spec.rb +47 -0
  183. data/lib/capybara/spec/session/has_field_spec.rb +139 -59
  184. data/lib/capybara/spec/session/has_link_spec.rb +47 -6
  185. data/lib/capybara/spec/session/has_none_selectors_spec.rb +42 -40
  186. data/lib/capybara/spec/session/has_select_spec.rb +107 -72
  187. data/lib/capybara/spec/session/has_selector_spec.rb +120 -71
  188. data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
  189. data/lib/capybara/spec/session/has_table_spec.rb +183 -5
  190. data/lib/capybara/spec/session/has_text_spec.rb +106 -62
  191. data/lib/capybara/spec/session/has_title_spec.rb +20 -14
  192. data/lib/capybara/spec/session/has_xpath_spec.rb +57 -38
  193. data/lib/capybara/spec/session/{headers.rb → headers_spec.rb} +3 -2
  194. data/lib/capybara/spec/session/html_spec.rb +14 -6
  195. data/lib/capybara/spec/session/matches_style_spec.rb +37 -0
  196. data/lib/capybara/spec/session/node_spec.rb +1024 -153
  197. data/lib/capybara/spec/session/node_wrapper_spec.rb +39 -0
  198. data/lib/capybara/spec/session/refresh_spec.rb +12 -6
  199. data/lib/capybara/spec/session/reset_session_spec.rb +82 -35
  200. data/lib/capybara/spec/session/{response_code.rb → response_code_spec.rb} +2 -1
  201. data/lib/capybara/spec/session/save_and_open_page_spec.rb +3 -2
  202. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +8 -12
  203. data/lib/capybara/spec/session/save_page_spec.rb +42 -55
  204. data/lib/capybara/spec/session/save_screenshot_spec.rb +16 -14
  205. data/lib/capybara/spec/session/screenshot_spec.rb +2 -2
  206. data/lib/capybara/spec/session/scroll_spec.rb +119 -0
  207. data/lib/capybara/spec/session/select_spec.rb +107 -81
  208. data/lib/capybara/spec/session/selectors_spec.rb +52 -19
  209. data/lib/capybara/spec/session/sibling_spec.rb +10 -10
  210. data/lib/capybara/spec/session/text_spec.rb +37 -21
  211. data/lib/capybara/spec/session/title_spec.rb +17 -5
  212. data/lib/capybara/spec/session/uncheck_spec.rb +43 -23
  213. data/lib/capybara/spec/session/unselect_spec.rb +39 -38
  214. data/lib/capybara/spec/session/visit_spec.rb +85 -53
  215. data/lib/capybara/spec/session/window/become_closed_spec.rb +24 -20
  216. data/lib/capybara/spec/session/window/current_window_spec.rb +5 -3
  217. data/lib/capybara/spec/session/window/open_new_window_spec.rb +5 -3
  218. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +27 -22
  219. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +12 -6
  220. data/lib/capybara/spec/session/window/window_spec.rb +97 -63
  221. data/lib/capybara/spec/session/window/windows_spec.rb +12 -10
  222. data/lib/capybara/spec/session/window/within_window_spec.rb +31 -86
  223. data/lib/capybara/spec/session/within_spec.rb +83 -44
  224. data/lib/capybara/spec/spec_helper.rb +54 -44
  225. data/lib/capybara/spec/test_app.rb +158 -43
  226. data/lib/capybara/spec/views/animated.erb +49 -0
  227. data/lib/capybara/spec/views/form.erb +163 -42
  228. data/lib/capybara/spec/views/frame_child.erb +4 -3
  229. data/lib/capybara/spec/views/frame_one.erb +2 -1
  230. data/lib/capybara/spec/views/frame_parent.erb +1 -1
  231. data/lib/capybara/spec/views/frame_two.erb +1 -1
  232. data/lib/capybara/spec/views/initial_alert.erb +2 -1
  233. data/lib/capybara/spec/views/layout.erb +10 -0
  234. data/lib/capybara/spec/views/obscured.erb +47 -0
  235. data/lib/capybara/spec/views/offset.erb +33 -0
  236. data/lib/capybara/spec/views/path.erb +2 -2
  237. data/lib/capybara/spec/views/popup_one.erb +1 -1
  238. data/lib/capybara/spec/views/popup_two.erb +1 -1
  239. data/lib/capybara/spec/views/react.erb +45 -0
  240. data/lib/capybara/spec/views/scroll.erb +21 -0
  241. data/lib/capybara/spec/views/spatial.erb +31 -0
  242. data/lib/capybara/spec/views/tables.erb +68 -1
  243. data/lib/capybara/spec/views/with_animation.erb +81 -0
  244. data/lib/capybara/spec/views/with_base_tag.erb +2 -2
  245. data/lib/capybara/spec/views/with_dragula.erb +24 -0
  246. data/lib/capybara/spec/views/with_fixed_header_footer.erb +2 -1
  247. data/lib/capybara/spec/views/with_hover.erb +3 -2
  248. data/lib/capybara/spec/views/with_hover1.erb +10 -0
  249. data/lib/capybara/spec/views/with_html.erb +69 -12
  250. data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
  251. data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
  252. data/lib/capybara/spec/views/with_js.erb +30 -5
  253. data/lib/capybara/spec/views/with_jstree.erb +26 -0
  254. data/lib/capybara/spec/views/with_namespace.erb +21 -0
  255. data/lib/capybara/spec/views/with_scope.erb +2 -2
  256. data/lib/capybara/spec/views/with_scope_other.erb +6 -0
  257. data/lib/capybara/spec/views/with_shadow.erb +31 -0
  258. data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
  259. data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
  260. data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
  261. data/lib/capybara/spec/views/with_windows.erb +1 -1
  262. data/lib/capybara/spec/views/within_frames.erb +5 -2
  263. data/lib/capybara/version.rb +2 -1
  264. data/lib/capybara/window.rb +36 -34
  265. data/lib/capybara.rb +134 -107
  266. data/spec/basic_node_spec.rb +60 -34
  267. data/spec/capybara_spec.rb +63 -88
  268. data/spec/counter_spec.rb +35 -0
  269. data/spec/css_builder_spec.rb +101 -0
  270. data/spec/css_splitter_spec.rb +38 -0
  271. data/spec/dsl_spec.rb +85 -64
  272. data/spec/filter_set_spec.rb +27 -9
  273. data/spec/fixtures/certificate.pem +25 -0
  274. data/spec/fixtures/key.pem +27 -0
  275. data/spec/fixtures/selenium_driver_rspec_failure.rb +6 -5
  276. data/spec/fixtures/selenium_driver_rspec_success.rb +6 -5
  277. data/spec/minitest_spec.rb +52 -7
  278. data/spec/minitest_spec_spec.rb +94 -63
  279. data/spec/per_session_config_spec.rb +14 -13
  280. data/spec/rack_test_spec.rb +194 -125
  281. data/spec/regexp_dissassembler_spec.rb +250 -0
  282. data/spec/result_spec.rb +111 -50
  283. data/spec/rspec/features_spec.rb +37 -31
  284. data/spec/rspec/scenarios_spec.rb +10 -8
  285. data/spec/rspec/shared_spec_matchers.rb +473 -422
  286. data/spec/rspec/views_spec.rb +5 -3
  287. data/spec/rspec_matchers_spec.rb +52 -11
  288. data/spec/rspec_spec.rb +109 -89
  289. data/spec/sauce_spec_chrome.rb +43 -0
  290. data/spec/selector_spec.rb +397 -68
  291. data/spec/selenium_spec_chrome.rb +187 -40
  292. data/spec/selenium_spec_chrome_remote.rb +96 -0
  293. data/spec/selenium_spec_edge.rb +60 -0
  294. data/spec/selenium_spec_firefox.rb +201 -41
  295. data/spec/selenium_spec_firefox_remote.rb +94 -0
  296. data/spec/selenium_spec_ie.rb +149 -0
  297. data/spec/selenium_spec_safari.rb +162 -0
  298. data/spec/server_spec.rb +213 -102
  299. data/spec/session_spec.rb +53 -16
  300. data/spec/shared_selenium_node.rb +79 -0
  301. data/spec/shared_selenium_session.rb +473 -122
  302. data/spec/spec_helper.rb +126 -7
  303. data/spec/whitespace_normalizer_spec.rb +54 -0
  304. data/spec/xpath_builder_spec.rb +93 -0
  305. metadata +355 -73
  306. data/.yard/templates_custom/default/class/html/selectors.erb +0 -38
  307. data/.yard/templates_custom/default/class/html/setup.rb +0 -17
  308. data/.yard/yard_extensions.rb +0 -78
  309. data/lib/capybara/query.rb +0 -7
  310. data/lib/capybara/rspec/compound.rb +0 -95
  311. data/lib/capybara/spec/session/assert_current_path.rb +0 -72
  312. data/lib/capybara/spec/session/assert_selector.rb +0 -148
  313. data/lib/capybara/spec/session/assert_text.rb +0 -234
  314. data/lib/capybara/spec/session/source_spec.rb +0 -0
  315. data/lib/capybara/spec/views/with_title.erb +0 -5
  316. data/spec/selenium_spec_marionette.rb +0 -127
@@ -1,87 +1,64 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  class Capybara::RackTest::Form < Capybara::RackTest::Node
3
4
  # This only needs to inherit from Rack::Test::UploadedFile because Rack::Test checks for
4
5
  # the class specifically when determining whether to construct the request as multipart.
5
6
  # That check should be based solely on the form element's 'enctype' attribute value,
6
7
  # which should probably be provided to Rack::Test in its non-GET request methods.
7
8
  class NilUploadedFile < Rack::Test::UploadedFile
8
- def initialize
9
- @empty_file = Tempfile.new("nil_uploaded_file")
9
+ def initialize # rubocop:disable Lint/MissingSuper
10
+ @empty_file = Tempfile.new('nil_uploaded_file')
10
11
  @empty_file.close
11
12
  end
12
13
 
13
- def original_filename; ""; end
14
- def content_type; "application/octet-stream"; end
14
+ def original_filename; ''; end
15
+ def content_type; 'application/octet-stream'; end
15
16
  def path; @empty_file.path; end
16
17
  def size; 0; end
17
- def read; ""; end
18
+ def read; ''; end
19
+ def append_to(_); end
20
+ def set_encoding(_); end # rubocop:disable Naming/AccessorMethodName
18
21
  end
19
22
 
20
23
  def params(button)
21
- params = make_params
22
-
23
- form_element_types=[:input, :select, :textarea]
24
- form_elements_xpath=XPath.generate do |x|
25
- xpath=x.descendant(*form_element_types).where(~x.attr(:form))
26
- xpath=xpath.union(x.anywhere(*form_element_types).where(x.attr(:form) == native[:id])) if native[:id]
27
- xpath.where(~x.attr(:disabled))
24
+ form_element_types = %i[input select textarea button]
25
+ form_elements_xpath = XPath.generate do |xp|
26
+ xpath = xp.descendant(*form_element_types).where(!xp.attr(:form))
27
+ xpath += xp.anywhere(*form_element_types).where(xp.attr(:form) == native[:id]) if native[:id]
28
+ xpath.where(!xp.attr(:disabled))
28
29
  end.to_s
29
30
 
30
- native.xpath(form_elements_xpath).map do |field|
31
+ form_elements = native.xpath(form_elements_xpath).reject { |el| submitter?(el) && (el != button.native) }
32
+
33
+ form_params = form_elements.each_with_object({}.compare_by_identity) do |field, params|
31
34
  case field.name
32
- when 'input'
33
- if %w(radio checkbox).include? field['type']
34
- if field['checked']
35
- node=Capybara::RackTest::Node.new(self.driver, field)
36
- merge_param!(params, field['name'].to_s, node.value.to_s)
37
- end
38
- elsif %w(submit image).include? field['type']
39
- # TO DO identify the click button here (in document order, rather
40
- # than leaving until the end of the params)
41
- elsif field['type'] =='file'
42
- if multipart?
43
- file = \
44
- if (value = field['value']).to_s.empty?
45
- NilUploadedFile.new
46
- else
47
- mime_info = MiniMime.lookup_by_filename(value)
48
- Rack::Test::UploadedFile.new(value, (mime_info && mime_info.content_type).to_s)
49
- end
50
- merge_param!(params, field['name'].to_s, file)
51
- else
52
- merge_param!(params, field['name'].to_s, File.basename(field['value'].to_s))
53
- end
54
- else
55
- merge_param!(params, field['name'].to_s, field['value'].to_s)
56
- end
57
- when 'select'
58
- if field['multiple'] == 'multiple'
59
- options = field.xpath(".//option[@selected]")
60
- options.each do |option|
61
- merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s)
62
- end
63
- else
64
- option = field.xpath(".//option[@selected]").first
65
- option ||= field.xpath('.//option').first
66
- merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s) if option
67
- end
68
- when 'textarea'
69
- merge_param!(params, field['name'].to_s, field['_capybara_raw_value'].to_s.gsub(/\n/, "\r\n"))
35
+ when 'input', 'button' then add_input_param(field, params)
36
+ when 'select' then add_select_param(field, params)
37
+ when 'textarea' then add_textarea_param(field, params)
70
38
  end
71
39
  end
72
- merge_param!(params, button[:name], button[:value] || "") if button[:name]
73
40
 
74
- params.to_params_hash
41
+ form_params.each_with_object(make_params) do |(name, value), params|
42
+ merge_param!(params, name, value)
43
+ end.to_params_hash
44
+
45
+ # form_elements.each_with_object(make_params) do |field, params|
46
+ # case field.name
47
+ # when 'input', 'button' then add_input_param(field, params)
48
+ # when 'select' then add_select_param(field, params)
49
+ # when 'textarea' then add_textarea_param(field, params)
50
+ # end
51
+ # end.to_params_hash
75
52
  end
76
53
 
77
54
  def submit(button)
78
- action = (button && button['formaction']) || native['action']
79
- method = (button && button['formmethod']) || request_method
80
- driver.submit(method, action.to_s, params(button))
55
+ action = button&.[]('formaction') || native['action']
56
+ method = button&.[]('formmethod') || request_method
57
+ driver.submit(method, action.to_s, params(button), content_type: native['enctype'])
81
58
  end
82
59
 
83
60
  def multipart?
84
- self[:enctype] == "multipart/form-data"
61
+ self[:enctype] == 'multipart/form-data'
85
62
  end
86
63
 
87
64
  private
@@ -93,10 +70,11 @@ private
93
70
  end
94
71
 
95
72
  def request_method
96
- self[:method] =~ /post/i ? :post : :get
73
+ /post/i.match?(self[:method] || '') ? :post : :get
97
74
  end
98
75
 
99
76
  def merge_param!(params, key, value)
77
+ key = key.to_s
100
78
  if Rack::Utils.respond_to?(:default_query_parser)
101
79
  Rack::Utils.default_query_parser.normalize_params(params, key, value, Rack::Utils.param_depth_limit)
102
80
  else
@@ -111,4 +89,61 @@ private
111
89
  ParamsHash.new
112
90
  end
113
91
  end
92
+
93
+ def add_input_param(field, params)
94
+ name, value = field['name'].to_s, field['value'].to_s
95
+ return if name.empty?
96
+
97
+ value = case field['type']
98
+ when 'radio', 'checkbox'
99
+ return unless field['checked']
100
+
101
+ Capybara::RackTest::Node.new(driver, field).value.to_s
102
+ when 'file'
103
+ return if value.empty? && params.keys.include?(name) && Rack::Test::VERSION.to_f >= 2.0 # rubocop:disable Performance/InefficientHashSearch
104
+
105
+ if multipart?
106
+ file_to_upload(value)
107
+ else
108
+ File.basename(value)
109
+ end
110
+ else
111
+ value
112
+ end
113
+ # merge_param!(params, name, value)
114
+ params[name] = value
115
+ end
116
+
117
+ def file_to_upload(filename)
118
+ if filename.empty?
119
+ NilUploadedFile.new
120
+ else
121
+ mime_info = MiniMime.lookup_by_filename(filename)
122
+ Rack::Test::UploadedFile.new(filename, mime_info&.content_type&.to_s)
123
+ end
124
+ end
125
+
126
+ def add_select_param(field, params)
127
+ name = field['name']
128
+ if field.has_attribute?('multiple')
129
+ value = field.xpath('.//option[@selected]').map do |option|
130
+ # merge_param!(params, field['name'], (option['value'] || option.text).to_s)
131
+ (option['value'] || option.text).to_s
132
+ end
133
+ params[name] = value unless value.empty?
134
+ else
135
+ option = field.xpath('.//option[@selected]').first || field.xpath('.//option').first
136
+ # merge_param!(params, field['name'], (option['value'] || option.text).to_s) if option
137
+ params[name] = (option['value'] || option.text).to_s if option
138
+ end
139
+ end
140
+
141
+ def add_textarea_param(field, params)
142
+ # merge_param!(params, field['name'], field['_capybara_raw_value'].to_s.gsub(/\r?\n/, "\r\n"))
143
+ params[field['name']] = field['_capybara_raw_value'].to_s.gsub(/\r?\n/, "\r\n")
144
+ end
145
+
146
+ def submitter?(el)
147
+ (%w[submit image].include? el['type']) || (el.name == 'button')
148
+ end
114
149
  end
@@ -1,79 +1,78 @@
1
1
  # frozen_string_literal: true
2
+
3
+ require 'capybara/rack_test/errors'
4
+ require 'capybara/node/whitespace_normalizer'
5
+
2
6
  class Capybara::RackTest::Node < Capybara::Driver::Node
7
+ include Capybara::Node::WhitespaceNormalizer
8
+
9
+ BLOCK_ELEMENTS = %w[p h1 h2 h3 h4 h5 h6 ol ul pre address blockquote dl div fieldset form hr noscript table].freeze
10
+
3
11
  def all_text
4
- Capybara::Helpers.normalize_whitespace(native.text)
12
+ normalize_spacing(native.text)
5
13
  end
6
14
 
7
15
  def visible_text
8
- Capybara::Helpers.normalize_whitespace(unnormalized_text)
16
+ normalize_visible_spacing(displayed_text)
9
17
  end
10
18
 
11
19
  def [](name)
12
20
  string_node[name]
13
21
  end
14
22
 
23
+ def style(_styles)
24
+ raise NotImplementedError, 'The rack_test driver does not process CSS'
25
+ end
26
+
15
27
  def value
16
28
  string_node.value
17
29
  end
18
30
 
19
- def set(value)
20
- return if disabled?
21
- if readonly?
22
- warn "Attempt to set readonly element with value: #{value} \n * This will raise an exception in a future version of Capybara"
23
- return
24
- end
31
+ def set(value, **options)
32
+ return if disabled? || readonly?
33
+
34
+ warn "Options passed to Node#set but the RackTest driver doesn't support any - ignoring" unless options.empty?
25
35
 
26
- if (Array === value) && !multiple?
27
- raise TypeError.new "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
36
+ if value.is_a?(Array) && !multiple?
37
+ raise TypeError, "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
28
38
  end
29
39
 
30
- if radio?
31
- set_radio(value)
32
- elsif checkbox?
33
- set_checkbox(value)
34
- elsif input_field?
35
- set_input(value)
36
- elsif textarea?
37
- native['_capybara_raw_value'] = value.to_s
40
+ if radio? then set_radio(value)
41
+ elsif checkbox? then set_checkbox(value)
42
+ elsif range? then set_range(value)
43
+ elsif input_field? then set_input(value)
44
+ elsif textarea? then native['_capybara_raw_value'] = value.to_s
38
45
  end
39
46
  end
40
47
 
41
48
  def select_option
42
49
  return if disabled?
43
- if select_node['multiple'] != 'multiple'
44
- select_node.find_xpath(".//option[@selected]").each { |node| node.native.remove_attribute("selected") }
45
- end
46
- native["selected"] = 'selected'
50
+
51
+ deselect_options unless select_node.multiple?
52
+ native['selected'] = 'selected'
47
53
  end
48
54
 
49
55
  def unselect_option
50
- if select_node['multiple'] != 'multiple'
51
- raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box."
52
- end
56
+ raise Capybara::UnselectNotAllowed, 'Cannot unselect option from single select box.' unless select_node.multiple?
57
+
53
58
  native.remove_attribute('selected')
54
59
  end
55
60
 
56
- def click
57
- if tag_name == 'a' && !self[:href].nil?
58
- method = self["data-method"] if driver.options[:respect_data_method]
59
- method ||= :get
60
- driver.follow(method, self[:href].to_s)
61
- elsif (tag_name == 'input' and %w(submit image).include?(type)) or
62
- ((tag_name == 'button') and type.nil? or type == "submit")
61
+ def click(keys = [], **options)
62
+ options.delete(:offset)
63
+ raise ArgumentError, 'The RackTest driver does not support click options' unless keys.empty? && options.empty?
64
+
65
+ if link?
66
+ follow_link
67
+ elsif submits?
63
68
  associated_form = form
64
69
  Capybara::RackTest::Form.new(driver, associated_form).submit(self) if associated_form
65
- elsif (tag_name == 'input' and %w(checkbox radio).include?(type))
70
+ elsif checkable?
66
71
  set(!checked?)
67
- elsif (tag_name == 'label')
68
- labelled_control = if native[:for]
69
- find_xpath("//input[@id='#{native[:for]}']").first
70
- else
71
- find_xpath(".//input").first
72
- end
73
-
74
- if labelled_control && (labelled_control.checkbox? || labelled_control.radio?)
75
- labelled_control.set(!labelled_control.checked?)
76
- end
72
+ elsif tag_name == 'label'
73
+ click_label
74
+ elsif (details = native.xpath('.//ancestor-or-self::details').last)
75
+ toggle_details(details)
77
76
  end
78
77
  end
79
78
 
@@ -94,47 +93,79 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
94
93
  end
95
94
 
96
95
  def disabled?
97
- if %w(option optgroup).include? tag_name
98
- string_node.disabled? || find_xpath("parent::*[self::optgroup or self::select]")[0].disabled?
96
+ return true if string_node.disabled?
97
+
98
+ if %w[option optgroup].include? tag_name
99
+ find_xpath(OPTION_OWNER_XPATH)[0].disabled?
99
100
  else
100
- string_node.disabled? || !find_xpath("parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]").empty?
101
+ !find_xpath(DISABLED_BY_FIELDSET_XPATH).empty?
101
102
  end
102
103
  end
103
104
 
105
+ def readonly?
106
+ # readonly attribute not valid on these input types
107
+ return false if input_field? && %w[hidden range color checkbox radio file submit image reset button].include?(type)
108
+
109
+ super
110
+ end
111
+
104
112
  def path
105
113
  native.path
106
114
  end
107
115
 
108
- def find_xpath(locator)
109
- native.xpath(locator).map { |n| self.class.new(driver, n) }
116
+ def find_xpath(locator, **_hints)
117
+ native.xpath(locator).map { |el| self.class.new(driver, el) }
110
118
  end
111
119
 
112
- def find_css(locator)
113
- native.css(locator, Capybara::RackTest::CSSHandlers.new).map { |n| self.class.new(driver, n) }
120
+ def find_css(locator, **_hints)
121
+ native.css(locator, Capybara::RackTest::CSSHandlers.new).map { |el| self.class.new(driver, el) }
114
122
  end
115
123
 
116
- def ==(other)
117
- native == other.native
124
+ public_instance_methods(false).each do |meth_name|
125
+ alias_method "unchecked_#{meth_name}", meth_name
126
+ private "unchecked_#{meth_name}" # rubocop:disable Style/AccessModifierDeclarations
127
+
128
+ class_eval <<~METHOD, __FILE__, __LINE__ + 1
129
+ def #{meth_name}(...)
130
+ stale_check
131
+ method(:"unchecked_#{meth_name}").call(...)
132
+ end
133
+ METHOD
118
134
  end
119
135
 
120
136
  protected
121
137
 
122
- def unnormalized_text(check_ancestor_visibility = true)
123
- if !string_node.visible?(check_ancestor_visibility)
138
+ # @api private
139
+ def displayed_text(check_ancestor: true)
140
+ if !string_node.visible?(check_ancestor)
124
141
  ''
125
142
  elsif native.text?
126
- native.text
143
+ native
144
+ .text
145
+ .delete(REMOVED_CHARACTERS)
146
+ .tr(SQUEEZED_SPACES, ' ')
147
+ .squeeze(' ')
127
148
  elsif native.element?
128
- native.children.map do |child|
129
- Capybara::RackTest::Node.new(driver, child).unnormalized_text(false)
130
- end.join
131
- else
149
+ text = native.children.map do |child|
150
+ Capybara::RackTest::Node.new(driver, child).displayed_text(check_ancestor: false)
151
+ end.join || ''
152
+ text = "\n#{text}\n" if BLOCK_ELEMENTS.include?(tag_name)
153
+ text
154
+ else # rubocop:disable Lint/DuplicateBranch
132
155
  ''
133
156
  end
134
157
  end
135
158
 
136
159
  private
137
160
 
161
+ def stale_check
162
+ raise Capybara::RackTest::Errors::StaleElementReferenceError unless native.document == driver.dom
163
+ end
164
+
165
+ def deselect_options
166
+ select_node.find_xpath('.//option[@selected]').each { |node| node.native.remove_attribute('selected') }
167
+ end
168
+
138
169
  def string_node
139
170
  @string_node ||= Capybara::Node::Simple.new(native)
140
171
  end
@@ -150,19 +181,19 @@ private
150
181
 
151
182
  def form
152
183
  if native[:form]
153
- native.xpath("//form[@id='#{native[:form]}']").first
184
+ native.xpath("//form[@id='#{native[:form]}']")
154
185
  else
155
- native.ancestors('form').first
156
- end
186
+ native.ancestors('form')
187
+ end.first
157
188
  end
158
189
 
159
- def set_radio(_value)
160
- other_radios_xpath = XPath.generate { |x| x.anywhere(:input)[x.attr(:name).equals(self[:name])] }.to_s
161
- driver.dom.xpath(other_radios_xpath).each { |node| node.remove_attribute("checked") }
190
+ def set_radio(_value) # rubocop:disable Naming/AccessorMethodName
191
+ other_radios_xpath = XPath.generate { |xp| xp.anywhere(:input)[xp.attr(:name) == self[:name]] }.to_s
192
+ driver.dom.xpath(other_radios_xpath).each { |node| node.remove_attribute('checked') }
162
193
  native['checked'] = 'checked'
163
194
  end
164
195
 
165
- def set_checkbox(value)
196
+ def set_checkbox(value) # rubocop:disable Naming/AccessorMethodName
166
197
  if value && !native['checked']
167
198
  native['checked'] = 'checked'
168
199
  elsif !value && native['checked']
@@ -170,25 +201,36 @@ private
170
201
  end
171
202
  end
172
203
 
173
- def set_input(value)
204
+ def set_range(value) # rubocop:disable Naming/AccessorMethodName
205
+ min, max, step = (native['min'] || 0).to_f, (native['max'] || 100).to_f, (native['step'] || 1).to_f
206
+ value = value.to_f
207
+ value = value.clamp(min, max)
208
+ value = (((value - min) / step).round * step) + min
209
+ native['value'] = value.clamp(min, max)
210
+ end
211
+
212
+ def set_input(value) # rubocop:disable Naming/AccessorMethodName
174
213
  if text_or_password? && attribute_is_not_blank?(:maxlength)
175
214
  # Browser behavior for maxlength="0" is inconsistent, so we stick with
176
215
  # Firefox, allowing no input
177
216
  value = value.to_s[0...self[:maxlength].to_i]
178
217
  end
179
- if Array === value #Assert multiple attribute is present
180
- value.each do |v|
218
+ if value.is_a?(Array) # Assert multiple attribute is present
219
+ value.each do |val|
181
220
  new_native = native.clone
182
221
  new_native.remove_attribute('value')
183
222
  native.add_next_sibling(new_native)
184
- new_native['value'] = v.to_s
223
+ new_native['value'] = val.to_s
185
224
  end
186
225
  native.remove
187
226
  else
188
- if readonly?
189
- warn "Attempt to set readonly element with value: #{value} \n *This will raise an exception in a future version of Capybara"
190
- else
191
- native['value'] = value.to_s
227
+ value.to_s.tap do |set_value|
228
+ if set_value.end_with?("\n") && form&.css('input, textarea')&.count == 1
229
+ native['value'] = set_value.to_s.chop
230
+ Capybara::RackTest::Form.new(driver, form).submit(self)
231
+ else
232
+ native['value'] = set_value
233
+ end
192
234
  end
193
235
  end
194
236
  end
@@ -197,25 +239,86 @@ private
197
239
  self[attribute] && !self[attribute].empty?
198
240
  end
199
241
 
242
+ def follow_link
243
+ method = self['data-method'] || self['data-turbo-method'] if driver.options[:respect_data_method]
244
+ method ||= :get
245
+ driver.follow(method, self[:href].to_s)
246
+ end
247
+
248
+ def click_label
249
+ labelled_control = if native[:for]
250
+ find_xpath("//input[@id='#{native[:for]}']")
251
+ else
252
+ find_xpath('.//input')
253
+ end.first
254
+
255
+ labelled_control.set(!labelled_control.checked?) if checkbox_or_radio?(labelled_control)
256
+ end
257
+
258
+ def toggle_details(details = nil)
259
+ details ||= native.xpath('.//ancestor-or-self::details').last
260
+ return unless details
261
+
262
+ if details.has_attribute?('open')
263
+ details.remove_attribute('open')
264
+ else
265
+ details.set_attribute('open', 'open')
266
+ end
267
+ end
268
+
269
+ def link?
270
+ tag_name == 'a' && !self[:href].nil?
271
+ end
272
+
273
+ def submits?
274
+ (tag_name == 'input' && %w[submit image].include?(type)) || (tag_name == 'button' && [nil, 'submit'].include?(type))
275
+ end
276
+
277
+ def checkable?
278
+ tag_name == 'input' && %w[checkbox radio].include?(type)
279
+ end
280
+
200
281
  protected
201
282
 
202
- def checkbox?
203
- input_field? && type == 'checkbox'
283
+ def checkbox_or_radio?(field = self)
284
+ field&.checkbox? || field&.radio?
204
285
  end
205
286
 
206
- def input_field?
207
- tag_name == 'input'
287
+ def checkbox?
288
+ input_field? && type == 'checkbox'
208
289
  end
209
290
 
210
291
  def radio?
211
292
  input_field? && type == 'radio'
212
293
  end
213
294
 
295
+ def text_or_password?
296
+ input_field? && (type == 'text' || type == 'password')
297
+ end
298
+
299
+ def input_field?
300
+ tag_name == 'input'
301
+ end
302
+
214
303
  def textarea?
215
- tag_name == "textarea"
304
+ tag_name == 'textarea'
216
305
  end
217
306
 
218
- def text_or_password?
219
- input_field? && (type == 'text' || type == 'password')
307
+ def range?
308
+ input_field? && type == 'range'
220
309
  end
310
+
311
+ OPTION_OWNER_XPATH = XPath.parent(:optgroup, :select, :datalist).to_s.freeze
312
+ DISABLED_BY_FIELDSET_XPATH = XPath.generate do |x|
313
+ x.parent(:fieldset)[
314
+ XPath.attr(:disabled)
315
+ ] + x.ancestor[
316
+ ~x.self(:legend) |
317
+ x.preceding_sibling(:legend)
318
+ ][
319
+ x.parent(:fieldset)[
320
+ x.attr(:disabled)
321
+ ]
322
+ ]
323
+ end.to_s.freeze
221
324
  end
@@ -1,14 +1,10 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'capybara/dsl'
3
4
 
4
5
  Capybara.app = Rack::Builder.new do
5
- map "/" do
6
- if Gem::Version.new(Rails.version) >= Gem::Version.new("3.0")
7
- run Rails.application
8
- else # Rails 2
9
- use Rails::Rack::Static
10
- run ActionController::Dispatcher.new
11
- end
6
+ map '/' do
7
+ run Rails.application
12
8
  end
13
9
  end.to_app
14
10
 
@@ -0,0 +1,41 @@
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, ...)
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
+ return @registered.public_send(method_name, ...)
23
+ end
24
+ super
25
+ end
26
+
27
+ def respond_to_missing?(method_name, include_all)
28
+ @registered.respond_to?(method_name) || super
29
+ end
30
+
31
+ private
32
+
33
+ def initialize
34
+ @registered = {}
35
+ end
36
+
37
+ def register(name, block)
38
+ @registered[name] = block
39
+ end
40
+ end
41
+ 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=new')
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