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,215 +1,566 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'spec_helper'
3
- require "selenium-webdriver"
4
+ require 'selenium-webdriver'
4
5
 
5
- RSpec.shared_examples "Capybara::Session" do |session, mode|
6
- let(:session) {session}
6
+ RSpec.shared_examples 'Capybara::Session' do |session, mode|
7
+ let(:session) { session }
7
8
 
8
9
  context 'with selenium driver' do
9
- before do
10
- @session = session
11
- end
12
-
13
10
  describe '#driver' do
14
- it "should be a selenium driver" do
15
- expect(@session.driver).to be_an_instance_of(Capybara::Selenium::Driver)
11
+ it 'should be a selenium driver' do
12
+ expect(session.driver).to be_an_instance_of(Capybara::Selenium::Driver)
16
13
  end
17
14
  end
18
15
 
19
16
  describe '#mode' do
20
- it "should remember the mode" do
21
- expect(@session.mode).to eq(mode)
17
+ it 'should remember the mode' do
18
+ expect(session.mode).to eq(mode)
22
19
  end
23
20
  end
24
21
 
25
- describe "#reset!" do
26
- it "freshly reset session should not be touched" do
27
- @session.instance_variable_set(:@touched, true)
28
- @session.reset!
29
- expect(@session.instance_variable_get(:@touched)).to eq false
22
+ describe '#reset!' do
23
+ it 'freshly reset session should not be touched' do
24
+ session.instance_variable_set(:@touched, true)
25
+ session.reset!
26
+ expect(session.instance_variable_get(:@touched)).to be false
30
27
  end
31
28
  end
32
29
 
33
- describe "exit codes" do
30
+ describe 'exit codes' do
31
+ let(:env) { { 'SELENIUM_BROWSER' => session.driver.options[:browser].to_s } }
32
+ let!(:orig_dir) { Dir.getwd }
33
+
34
34
  before do
35
- @current_dir = Dir.getwd
36
35
  Dir.chdir(File.join(File.dirname(__FILE__), '..'))
37
- @env = { 'SELENIUM_BROWSER' => @session.driver.options[:browser].to_s }
38
- @env['LEGACY_FIREFOX'] = 'TRUE' if mode == :selenium_firefox
39
36
  end
40
37
 
41
38
  after do
42
- Dir.chdir(@current_dir)
39
+ Dir.chdir(orig_dir)
43
40
  end
44
41
 
45
- it "should have return code 1 when running selenium_driver_rspec_failure.rb" do
46
- skip if ENV['HEADLESS']
47
- system(@env, 'rspec spec/fixtures/selenium_driver_rspec_failure.rb', out: File::NULL, err: File::NULL)
48
- expect($?.exitstatus).to eq(1)
42
+ it 'should have return code 1 when running selenium_driver_rspec_failure.rb' do
43
+ skip 'only setup for local non-headless' if headless_or_remote?
44
+ skip 'Not setup for edge' if edge?(session)
45
+
46
+ system(env, 'rspec spec/fixtures/selenium_driver_rspec_failure.rb', out: File::NULL, err: File::NULL)
47
+ expect($CHILD_STATUS.exitstatus).to eq(1)
49
48
  end
50
49
 
51
- it "should have return code 0 when running selenium_driver_rspec_success.rb" do
52
- skip if ENV['HEADLESS']
53
- system(@env, 'rspec spec/fixtures/selenium_driver_rspec_success.rb', out: File::NULL, err: File::NULL)
54
- expect($?.exitstatus).to eq(0)
50
+ it 'should have return code 0 when running selenium_driver_rspec_success.rb' do
51
+ skip 'only setup for local non-headless' if headless_or_remote?
52
+ skip 'Not setup for edge' if edge?(session)
53
+
54
+ system(env, 'rspec spec/fixtures/selenium_driver_rspec_success.rb', out: File::NULL, err: File::NULL)
55
+ expect($CHILD_STATUS.exitstatus).to eq(0)
55
56
  end
56
57
  end
57
58
 
58
- describe "#accept_alert" do
59
- it "supports a blockless mode" do
60
- @session.visit('/with_js')
61
- @session.click_link('Open alert')
62
- @session.accept_alert
63
- expect{@session.driver.browser.switch_to.alert}.to raise_error(@session.driver.send(:modal_error))
59
+ describe '#accept_alert', requires: [:modals] do
60
+ it 'supports a blockless mode' do
61
+ session.visit('/with_js')
62
+ session.click_link('Open alert')
63
+ session.accept_alert
64
+ expect { session.driver.browser.switch_to.alert }.to raise_error(session.driver.send(:modal_error))
64
65
  end
65
66
 
66
- it "can be called before visiting" do
67
- @session.accept_alert "Initial alert" do
68
- @session.visit('/initial_alert')
67
+ it 'can be called before visiting' do
68
+ session.accept_alert 'Initial alert' do
69
+ session.visit('/initial_alert')
69
70
  end
70
- expect(@session).to have_text('Initial alert page')
71
+ expect(session).to have_text('Initial alert page')
71
72
  end
72
73
  end
73
74
 
74
- context '#fill_in_with empty string and no options' do
75
+ describe '#fill_in_with empty string and no options' do
75
76
  it 'should trigger change when clearing a field' do
76
- @session.visit('/with_js')
77
- @session.fill_in('with_change_event', with: '')
77
+ session.visit('/with_js')
78
+ session.fill_in('with_change_event', with: '')
78
79
  # click outside the field to trigger the change event
79
- @session.find(:css, 'body').click
80
- expect(@session).to have_selector(:css, '.change_event_triggered', match: :one)
80
+ session.find(:css, 'body').click
81
+ expect(session).to have_selector(:css, '.change_event_triggered', match: :one)
81
82
  end
82
83
  end
83
84
 
84
- context "#fill_in with { :clear => :backspace } fill_option", requires: [:js] do
85
+ describe '#fill_in with { :clear => :backspace } fill_option', requires: [:js] do
86
+ before do
87
+ # Firefox has an issue with change events if the main window doesn't think it's focused
88
+ session.execute_script('window.focus()')
89
+ end
90
+
85
91
  it 'should fill in a field, replacing an existing value' do
86
- @session.visit('/form')
87
- @session.fill_in('form_first_name', with: 'Harry',
88
- fill_options: { clear: :backspace} )
89
- expect(@session.find(:fillable_field, 'form_first_name').value).to eq('Harry')
92
+ session.visit('/form')
93
+ session.fill_in('form_first_name',
94
+ with: 'Harry',
95
+ fill_options: { clear: :backspace })
96
+ expect(session.find(:fillable_field, 'form_first_name').value).to eq('Harry')
97
+ end
98
+
99
+ it 'should fill in a field, replacing an existing value, even with caret position' do
100
+ session.visit('/form')
101
+ session.find(:css, '#form_first_name').execute_script <<-JS
102
+ this.focus();
103
+ this.setSelectionRange(0, 0);
104
+ JS
105
+
106
+ session.fill_in('form_first_name',
107
+ with: 'Harry',
108
+ fill_options: { clear: :backspace })
109
+ expect(session.find(:fillable_field, 'form_first_name').value).to eq('Harry')
110
+ end
111
+
112
+ it 'should fill in if the option is set via global option' do
113
+ Capybara.default_set_options = { clear: :backspace }
114
+ session.visit('/form')
115
+ session.fill_in('form_first_name', with: 'Thomas')
116
+ expect(session.find(:fillable_field, 'form_first_name').value).to eq('Thomas')
90
117
  end
91
118
 
92
119
  it 'should only trigger onchange once' do
93
- @session.visit('/with_js')
94
- @session.fill_in('with_change_event', with: 'some value',
95
- fill_options: { :clear => :backspace })
120
+ session.visit('/with_js')
121
+ sleep 2 if safari?(session) # Safari needs a delay (to load event handlers maybe ???)
122
+ session.fill_in('with_change_event',
123
+ with: 'some value',
124
+ fill_options: { clear: :backspace })
96
125
  # click outside the field to trigger the change event
97
- @session.find(:css, 'body').click
98
- expect(@session.find(:css, '.change_event_triggered', match: :one)).to have_text 'some value'
126
+ session.find(:css, '#with_focus_event').click
127
+ expect(session.find(:css, '.change_event_triggered', match: :one, wait: 5)).to have_text 'some value'
99
128
  end
100
129
 
101
130
  it 'should trigger change when clearing field' do
102
- @session.visit('/with_js')
103
- @session.fill_in('with_change_event', with: '',
104
- fill_options: { :clear => :backspace })
131
+ session.visit('/with_js')
132
+ session.fill_in('with_change_event',
133
+ with: '',
134
+ fill_options: { clear: :backspace })
105
135
  # click outside the field to trigger the change event
106
- @session.find(:css, 'body').click
107
- expect(@session).to have_selector(:css, '.change_event_triggered', match: :one)
136
+ session.find(:css, '#with_focus_event').click
137
+ expect(session).to have_selector(:css, '.change_event_triggered', match: :one, wait: 5)
108
138
  end
109
139
 
110
140
  it 'should trigger input event field_value.length times' do
111
- @session.visit('/with_js')
112
- @session.fill_in('with_change_event', with: '',
113
- fill_options: { :clear => :backspace })
141
+ session.visit('/with_js')
142
+ session.fill_in('with_change_event',
143
+ with: '',
144
+ fill_options: { clear: :backspace })
114
145
  # click outside the field to trigger the change event
115
- @session.find(:css, 'body').click
116
- expect(@session).to have_xpath('//p[@class="input_event_triggered"]', count: 13)
146
+ # session.find(:css, 'body').click
147
+ session.find(:css, 'h1', text: 'FooBar').click
148
+ expect(session).to have_xpath('//p[@class="input_event_triggered"]', count: 13)
117
149
  end
118
150
  end
119
151
 
120
- context "#fill_in with { clear: :none } fill_options" do
152
+ describe '#fill_in with { clear: :none } fill_options' do
121
153
  it 'should append to content in a field' do
122
- @session.visit('/form')
123
- @session.fill_in('form_first_name', with: 'Harry',
124
- fill_options: { clear: :none} )
125
- expect(@session.find(:fillable_field, 'form_first_name').value).to eq('JohnHarry')
154
+ pending 'Safari overwrites by default - need to figure out a workaround' if safari?(session)
155
+
156
+ session.visit('/form')
157
+ session.fill_in('form_first_name',
158
+ with: 'Harry',
159
+ fill_options: { clear: :none })
160
+ expect(session.find(:fillable_field, 'form_first_name').value).to eq('JohnHarry')
161
+ end
162
+
163
+ it 'works with rapid fill' do
164
+ pending 'Safari overwrites by default - need to figure out a workaround' if safari?(session)
165
+
166
+ long_string = (0...60).map { |i| ((i % 26) + 65).chr }.join
167
+ session.visit('/form')
168
+ session.fill_in('form_first_name', with: long_string, fill_options: { clear: :none })
169
+ expect(session.find(:fillable_field, 'form_first_name').value).to eq("John#{long_string}")
170
+ end
171
+ end
172
+
173
+ describe '#fill_in with Date' do
174
+ before do
175
+ session.visit('/form')
176
+ session.find(:css, '#form_date').execute_script <<-JS
177
+ window.capybara_formDateFiredEvents = [];
178
+ var fd = this;
179
+ ['focus', 'input', 'change'].forEach(function(eventType) {
180
+ fd.addEventListener(eventType, function() { window.capybara_formDateFiredEvents.push(eventType); });
181
+ });
182
+ JS
183
+ # work around weird FF issue where it would create an extra focus issue in some cases
184
+ session.find(:css, 'h1', text: 'Form').click
185
+ # session.find(:css, 'body').click
186
+ end
187
+
188
+ it 'should generate standard events on changing value' do
189
+ pending "IE 11 doesn't support date input type" if ie?(session)
190
+ session.fill_in('form_date', with: Date.today)
191
+ expect(session.evaluate_script('window.capybara_formDateFiredEvents')).to eq %w[focus input change]
192
+ end
193
+
194
+ it 'should not generate input and change events if the value is not changed' do
195
+ pending "IE 11 doesn't support date input type" if ie?(session)
196
+ session.fill_in('form_date', with: Date.today)
197
+ session.fill_in('form_date', with: Date.today)
198
+ # Chrome adds an extra focus for some reason - ok for now
199
+ expect(session.evaluate_script('window.capybara_formDateFiredEvents')).to eq(%w[focus input change])
126
200
  end
127
201
  end
128
202
 
129
- context "#fill_in with { clear: Array } fill_options" do
203
+ describe '#fill_in with { clear: Array } fill_options' do
130
204
  it 'should pass the array through to the element' do
131
- pending "selenium-webdriver/geckodriver doesn't support complex sets of characters" if marionette?(@session)
132
- #this is mainly for use with [[:control, 'a'], :backspace] - however since that is platform dependant I'm testing with something less useful
133
- @session.visit('/form')
134
- @session.fill_in('form_first_name', with: 'Harry',
135
- fill_options: { clear: [[:shift, 'abc'], :backspace] } )
136
- expect(@session.find(:fillable_field, 'form_first_name').value).to eq('JohnABHarry')
205
+ # this is mainly for use with [[:control, 'a'], :backspace] - however since that is platform dependant I'm testing with something less useful
206
+ session.visit('/form')
207
+ session.fill_in('form_first_name',
208
+ with: 'Harry',
209
+ fill_options: { clear: [[:shift, 'abc'], :backspace] })
210
+ expect(session.find(:fillable_field, 'form_first_name').value).to eq('JohnABHarry')
211
+ end
212
+ end
213
+
214
+ describe '#fill_in with Emoji' do
215
+ it 'sends emojis' do
216
+ session.visit('/form')
217
+ session.fill_in('form_first_name', with: 'a😀cd😴 🛌🏽🇵🇹 e🤾🏽‍♀️f')
218
+ expect(session.find(:fillable_field, 'form_first_name').value).to eq('a😀cd😴 🛌🏽🇵🇹 e🤾🏽‍♀️f')
219
+ end
220
+ end
221
+
222
+ describe '#send_keys' do
223
+ it 'defaults to sending keys to the active_element' do
224
+ session.visit('/form')
225
+
226
+ expect(session.active_element).to match_selector(:css, 'body')
227
+
228
+ session.send_keys(:tab)
229
+
230
+ expect(session.active_element).to match_selector(:css, '[tabindex="1"]')
137
231
  end
138
232
  end
139
233
 
140
- describe "#path" do
141
- it "returns xpath" do
234
+ describe '#path' do
235
+ it 'returns xpath' do
142
236
  # this is here because it is testing for an XPath that is specific to the algorithm used in the selenium driver
143
- @session.visit('/path')
144
- element = @session.find(:link, 'Second Link')
145
- expect(element.path).to eq('/html/body/div[2]/a[1]')
237
+ session.visit('/path')
238
+ element = session.find(:link, 'Second Link')
239
+ expect(element.path).to eq('/HTML/BODY[1]/DIV[2]/A[1]')
240
+ end
241
+
242
+ it 'handles namespaces in xhtml' do
243
+ pending "IE 11 doesn't handle all XPath querys (namespace-uri, etc)" if ie?(session)
244
+ session.visit '/with_namespace'
245
+ rect = session.find(:css, 'div svg rect:first-of-type')
246
+ expect(rect.path).to eq("/HTML/BODY[1]/DIV[1]/*[local-name()='svg' and namespace-uri()='http://www.w3.org/2000/svg'][1]/*[local-name()='rect' and namespace-uri()='http://www.w3.org/2000/svg'][1]")
247
+ expect(session.find(:xpath, rect.path)).to eq rect
248
+ end
249
+
250
+ it 'handles default namespaces in html5' do
251
+ pending "IE 11 doesn't handle all XPath querys (namespace-uri, etc)" if ie?(session)
252
+ session.visit '/with_html5_svg'
253
+ rect = session.find(:css, 'div svg rect:first-of-type')
254
+ expect(rect.path).to eq("/HTML/BODY[1]/DIV[1]/*[local-name()='svg' and namespace-uri()='http://www.w3.org/2000/svg'][1]/*[local-name()='rect' and namespace-uri()='http://www.w3.org/2000/svg'][1]")
255
+ expect(session.find(:xpath, rect.path)).to eq rect
256
+ end
257
+
258
+ it 'handles case sensitive element names' do
259
+ pending "IE 11 doesn't handle all XPath querys (namespace-uri, etc)" if ie?(session)
260
+ session.visit '/with_namespace'
261
+ els = session.all(:css, 'div *', visible: :all)
262
+ expect { els.map(&:path) }.not_to raise_error
263
+ lg = session.find(:css, 'div linearGradient', visible: :all)
264
+ expect(session.find(:xpath, lg.path, visible: :all)).to eq lg
146
265
  end
147
266
  end
148
267
 
149
- describe "all with disappearing elements" do
150
- it "ignores stale elements in results" do
151
- @session.visit('/path')
152
- elements = @session.all(:link) { |node| raise Selenium::WebDriver::Error::StaleElementReferenceError }
268
+ describe 'all with disappearing elements' do
269
+ it 'ignores stale elements in results' do
270
+ session.visit('/path')
271
+ elements = session.all(:link) { |_node| raise Selenium::WebDriver::Error::StaleElementReferenceError }
153
272
  expect(elements.size).to eq 0
154
273
  end
155
274
  end
156
275
 
157
- describe "#evaluate_script" do
158
- it "can return an element" do
159
- @session.visit('/form')
160
- element = @session.evaluate_script("document.getElementById('form_title')")
161
- expect(element).to eq @session.find(:id, 'form_title')
276
+ describe '#evaluate_script' do
277
+ it 'can return an element' do
278
+ session.visit('/form')
279
+ element = session.evaluate_script("document.getElementById('form_title')")
280
+ expect(element).to eq session.find(:id, 'form_title')
162
281
  end
163
282
 
164
- it "can return arrays of nested elements" do
165
- @session.visit('/form')
166
- elements = @session.evaluate_script('document.querySelectorAll("#form_city option")')
167
- elements.each do |el|
168
- expect(el).to be_instance_of Capybara::Node::Element
169
- end
170
- expect(elements).to eq @session.find(:css, '#form_city').all(:css, 'option').to_a
283
+ it 'returns a shadow root' do
284
+ session.visit('/with_shadow')
285
+ shadow = session.find(:css, '#shadow_host')
286
+ element = session.evaluate_script('arguments[0].shadowRoot', shadow)
287
+ expect(element).to be_instance_of(Capybara::Node::Element)
171
288
  end
172
289
 
173
- it "can return hashes with elements" do
174
- @session.visit('/form')
175
- result = @session.evaluate_script("{ a: document.getElementById('form_title'), b: {c: document.querySelectorAll('#form_city option')}}")
176
- expect(result).to eq({
177
- 'a' => @session.find(:id, 'form_title'),
290
+ it 'can return arrays of nested elements' do
291
+ session.visit('/form')
292
+ elements = session.evaluate_script('document.querySelectorAll("#form_city option")')
293
+ expect(elements).to all(be_instance_of Capybara::Node::Element)
294
+ expect(elements).to eq session.find(:css, '#form_city').all(:css, 'option').to_a
295
+ end
296
+
297
+ it 'can return hashes with elements' do
298
+ session.visit('/form')
299
+ result = session.evaluate_script("{ a: document.getElementById('form_title'), b: {c: document.querySelectorAll('#form_city option')}}")
300
+ expect(result).to eq(
301
+ 'a' => session.find(:id, 'form_title'),
178
302
  'b' => {
179
- 'c' => @session.find(:css, '#form_city').all(:css, 'option').to_a
303
+ 'c' => session.find(:css, '#form_city').all(:css, 'option').to_a
180
304
  }
181
- })
305
+ )
182
306
  end
183
307
 
184
- describe "#evaluate_async_script" do
185
- it "will timeout if the script takes too long" do
186
- @session.visit('/with_js')
308
+ describe '#evaluate_async_script' do
309
+ it 'will timeout if the script takes too long' do
310
+ skip 'safaridriver returns the wrong error type' if safari?(session)
311
+ session.visit('/with_js')
187
312
  expect do
188
- @session.using_wait_time(1) do
189
- @session.evaluate_async_script("var cb = arguments[0]; setTimeout(function(){ cb(null) }, 3000)")
313
+ session.using_wait_time(1) do
314
+ session.evaluate_async_script('var cb = arguments[0]; setTimeout(function(){ cb(null) }, 3000)')
190
315
  end
191
316
  end.to raise_error Selenium::WebDriver::Error::ScriptTimeoutError
192
317
  end
193
318
  end
194
319
  end
195
320
 
196
- describe "Element#inspect" do
197
- it "outputs obsolete elements" do
198
- @session.visit('/form')
199
- el = @session.find(:button, 'Click me!').click
200
- expect(@session).to have_no_button('Click me!')
201
- expect(el).not_to receive(:synchronize)
202
- expect(el.inspect).to eq "Obsolete #<Capybara::Node::Element>"
321
+ describe 'Element#inspect' do
322
+ it 'outputs obsolete elements' do
323
+ session.visit('/form')
324
+ el = session.find(:button, 'Click me!').click
325
+ expect(session).to have_no_button('Click me!')
326
+ allow(el).to receive(:synchronize)
327
+ expect(el.inspect).to eq 'Obsolete #<Capybara::Node::Element>'
328
+ expect(el).not_to have_received(:synchronize)
329
+ end
330
+ end
331
+
332
+ describe 'Element#click' do
333
+ it 'should handle fixed headers/footers' do
334
+ session.visit('/with_fixed_header_footer')
335
+ session.using_wait_time(2) do
336
+ session.find(:link, 'Go to root').click
337
+ end
338
+ expect(session).to have_current_path('/')
339
+ end
340
+ end
341
+
342
+ describe 'Capybara#Node#attach_file' do
343
+ it 'can attach a directory' do
344
+ pending "Geckodriver doesn't support uploading a directory" if firefox?(session)
345
+ pending "Selenium remote doesn't support transferring a directory" if remote?(session)
346
+ pending "Headless Chrome doesn't support directory upload - https://bugs.chromium.org/p/chromedriver/issues/detail?id=2521&q=directory%20upload&colspec=ID%20Status%20Pri%20Owner%20Summary" if chrome?(session) && chrome_lt?(110, session) && ENV.fetch('HEADLESS', nil)
347
+ pending "IE doesn't support uploading a directory" if ie?(session)
348
+ pending 'Chrome/chromedriver 73 breaks this' if chrome?(session) && chrome_gte?(73, session) && chrome_lt?(75, session)
349
+
350
+ session.visit('/form')
351
+ test_file_dir = File.expand_path('./fixtures', File.dirname(__FILE__))
352
+ session.attach_file('Directory Upload', test_file_dir)
353
+ session.click_button('Upload Multiple')
354
+ expect(session).to have_text('5 | ') # number of files
355
+ end
356
+
357
+ it 'can attach a relative file' do
358
+ pending 'Geckdoriver on windows requires alternate file separator which path expansion replaces' if Gem.win_platform? && firefox?(session)
359
+
360
+ session.visit('/form')
361
+ session.attach_file('Single Document', 'spec/fixtures/capybara.csv')
362
+ session.click_button('Upload Single')
363
+ expect(session).to have_text('Content-type: text/csv')
203
364
  end
204
365
  end
205
366
 
206
- describe "Element#click" do
207
- it "should handle fixed headers/footers" do
208
- @session.visit('/with_fixed_header_footer')
209
- # @session.click_link('Go to root')
210
- @session.find(:link, 'Go to root').click
211
- expect(@session).to have_current_path('/')
367
+ context 'Windows' do
368
+ it "can't close the primary window" do
369
+ expect do
370
+ session.current_window.close
371
+ end.to raise_error(ArgumentError, 'Not allowed to close the primary window')
212
372
  end
213
373
  end
374
+
375
+ # rubocop:disable RSpec/InstanceVariable
376
+ describe 'Capybara#disable_animation' do
377
+ context 'when set to `true`' do
378
+ before(:context) do # rubocop:disable RSpec/BeforeAfterAll
379
+ skip "Safari doesn't support multiple sessions" if safari?(session)
380
+ # NOTE: Although Capybara.SpecHelper.reset! sets Capybara.disable_animation to false,
381
+ # it doesn't affect any of these tests because the settings are applied per-session
382
+ Capybara.disable_animation = true
383
+ @animation_session = Capybara::Session.new(session.mode, TestApp.new)
384
+ end
385
+
386
+ it 'should add CSS to the <head> element' do
387
+ @animation_session.visit('with_animation')
388
+
389
+ expect(@animation_session).to have_selector(:css, 'head > style', text: 'transition: none', visible: :hidden)
390
+ end
391
+
392
+ it 'should disable CSS transitions' do
393
+ @animation_session.visit('with_animation')
394
+ @animation_session.click_link('transition me away')
395
+ expect(@animation_session).to have_no_link('transition me away', wait: 0.5)
396
+ end
397
+
398
+ it 'should disable CSS animations (set to 0s)' do
399
+ @animation_session.visit('with_animation')
400
+ sleep 1
401
+ @animation_session.click_link('animate me away')
402
+ expect(@animation_session).to have_no_link('animate me away', wait: 0.5)
403
+ end
404
+
405
+ it 'should disable CSS animations on pseudo elements (set to 0s)' do
406
+ @animation_session.visit('with_animation')
407
+ sleep 1
408
+ @animation_session.find_link('animate me away').right_click
409
+ expect(@animation_session).to have_content('Animation Ended', wait: 0.1)
410
+ end
411
+
412
+ it 'should scroll the page instantly', requires: [:js] do
413
+ @animation_session.visit('with_animation')
414
+ scroll_y = @animation_session.evaluate_script(<<~JS)
415
+ (function(){
416
+ window.scrollTo(0,500);
417
+ return window.scrollY;
418
+ })()
419
+ JS
420
+ expect(scroll_y).to eq 500
421
+ end
422
+
423
+ it 'should scroll the page instantly without jquery animation', requires: [:js] do
424
+ @animation_session.visit('with_jquery_animation')
425
+ @animation_session.click_link('scroll top 500')
426
+ scroll_y = @animation_session.evaluate_script('window.scrollY')
427
+ expect(scroll_y).to eq 500
428
+ end
429
+ end
430
+
431
+ context 'when set to `false`' do
432
+ before(:context) do # rubocop:disable RSpec/BeforeAfterAll
433
+ skip "Safari doesn't support multiple sessions" if safari?(session)
434
+ # NOTE: Although Capybara.SpecHelper.reset! sets Capybara.disable_animation to false,
435
+ # it doesn't affect any of these tests because the settings are applied per-session
436
+ Capybara.disable_animation = false
437
+ @animation_session = Capybara::Session.new(session.mode, TestApp.new)
438
+ end
439
+
440
+ it 'should scroll the page with a smooth animation', requires: [:js] do
441
+ @animation_session.visit('with_animation')
442
+ scroll_y = @animation_session.evaluate_script(<<~JS)
443
+ (function(){
444
+ window.scrollTo(0,500);
445
+ return window.scrollY;
446
+ })()
447
+ JS
448
+ # measured over 0.5 seconds: 0, 75, 282, 478, 500
449
+ expect(scroll_y).to be < 500
450
+ end
451
+
452
+ it 'should scroll the page with jquery animation', requires: [:js] do
453
+ @animation_session.visit('with_jquery_animation')
454
+ @animation_session.click_link('scroll top 500')
455
+ scroll_y = @animation_session.evaluate_script('window.scrollY')
456
+ expect(scroll_y).to be < 500
457
+ end
458
+ end
459
+
460
+ context 'if we pass in css that matches elements' do
461
+ before(:context) do # rubocop:disable RSpec/BeforeAfterAll
462
+ skip "safaridriver doesn't support multiple sessions" if safari?(session)
463
+ # NOTE: Although Capybara.SpecHelper.reset! sets Capybara.disable_animation to false,
464
+ # it doesn't affect any of these tests because the settings are applied per-session
465
+ Capybara.disable_animation = '#with_animation a'
466
+ @animation_session_with_matching_css = Capybara::Session.new(session.mode, TestApp.new)
467
+ end
468
+
469
+ it 'should disable CSS transitions' do
470
+ @animation_session_with_matching_css.visit('with_animation')
471
+ sleep 1
472
+ @animation_session_with_matching_css.click_link('transition me away')
473
+ expect(@animation_session_with_matching_css).to have_no_link('transition me away', wait: 0.5)
474
+ end
475
+
476
+ it 'should disable CSS animations' do
477
+ @animation_session_with_matching_css.visit('with_animation')
478
+ sleep 1
479
+ @animation_session_with_matching_css.click_link('animate me away')
480
+ expect(@animation_session_with_matching_css).to have_no_link('animate me away', wait: 0.5)
481
+ end
482
+ end
483
+
484
+ context 'if we pass in css that does not match elements' do
485
+ before(:context) do # rubocop:disable RSpec/BeforeAfterAll
486
+ skip "Safari doesn't support multiple sessions" if safari?(session)
487
+ # NOTE: Although Capybara.SpecHelper.reset! sets Capybara.disable_animation to false,
488
+ # it doesn't affect any of these tests because the settings are applied per-session
489
+ Capybara.disable_animation = '.this-class-matches-nothing'
490
+ @animation_session_without_matching_css = Capybara::Session.new(session.mode, TestApp.new)
491
+ end
492
+
493
+ it 'should not disable CSS transitions' do
494
+ @animation_session_without_matching_css.visit('with_animation')
495
+ sleep 1
496
+ @animation_session_without_matching_css.click_link('transition me away')
497
+ sleep 0.5 # Wait long enough for click to have been processed
498
+ expect(@animation_session_without_matching_css).to have_link('transition me away', wait: false)
499
+ expect(@animation_session_without_matching_css).to have_no_link('transition me away', wait: 5)
500
+ end
501
+
502
+ it 'should not disable CSS animations' do
503
+ @animation_session_without_matching_css.visit('with_animation')
504
+ sleep 1
505
+ @animation_session_without_matching_css.click_link('animate me away')
506
+ sleep 0.5 # Wait long enough for click to have been processed
507
+ expect(@animation_session_without_matching_css).to have_link('animate me away', wait: false)
508
+ expect(@animation_session_without_matching_css).to have_no_link('animate me away', wait: 5)
509
+ end
510
+ end
511
+ end
512
+ # rubocop:enable RSpec/InstanceVariable
513
+
514
+ describe ':element selector' do
515
+ it 'can find html5 svg elements' do
516
+ session.visit('with_html5_svg')
517
+ expect(session).to have_selector(:element, :svg)
518
+ expect(session).to have_selector(:element, :rect, visible: :visible)
519
+ expect(session).to have_selector(:element, :circle)
520
+ expect(session).to have_selector(:element, :linearGradient, visible: :all)
521
+ end
522
+
523
+ it 'can query attributes with strange characters' do
524
+ session.visit('/form')
525
+ expect(session).to have_selector(:element, '{custom}': true)
526
+ expect(session).to have_selector(:element, '{custom}': 'abcdef')
527
+ end
528
+ end
529
+
530
+ describe 'with react' do
531
+ context 'controlled components' do
532
+ it 'can set and clear a text field' do
533
+ skip "This test doesn't support older browsers" if ie?(session)
534
+
535
+ session.visit 'react'
536
+
537
+ session.fill_in('Name:', with: 'abc')
538
+ session.accept_prompt 'A name was submitted: abc' do
539
+ session.click_button('Submit')
540
+ end
541
+
542
+ session.fill_in('Name:', with: '')
543
+ session.accept_prompt(/A name was submitted: $/) do
544
+ session.click_button('Submit')
545
+ end
546
+ end
547
+
548
+ it 'works with rapid fill' do
549
+ skip "This test doesn't support older browsers" if ie?(session)
550
+
551
+ session.visit 'react'
552
+ long_string = (0...60).map { |i| ((i % 26) + 65).chr }.join
553
+
554
+ session.fill_in('Name:', with: long_string)
555
+ session.accept_prompt "A name was submitted: #{long_string}" do
556
+ session.click_button('Submit')
557
+ end
558
+ end
559
+ end
560
+ end
561
+ end
562
+
563
+ def headless_or_remote?
564
+ !ENV['HEADLESS'].nil? || session.driver.options[:browser] == :remote
214
565
  end
215
566
  end