capybara 3.0.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 (312) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/History.md +891 -12
  4. data/License.txt +1 -1
  5. data/README.md +257 -84
  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 +38 -9
  10. data/lib/capybara/dsl.rb +9 -7
  11. data/lib/capybara/helpers.rb +57 -8
  12. data/lib/capybara/minitest/spec.rb +185 -84
  13. data/lib/capybara/minitest.rb +264 -145
  14. data/lib/capybara/node/actions.rb +248 -124
  15. data/lib/capybara/node/base.rb +35 -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 +350 -113
  19. data/lib/capybara/node/finders.rb +104 -82
  20. data/lib/capybara/node/matchers.rb +363 -157
  21. data/lib/capybara/node/simple.rb +54 -15
  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 +9 -10
  25. data/lib/capybara/queries/base_query.rb +25 -18
  26. data/lib/capybara/queries/current_path_query.rb +16 -6
  27. data/lib/capybara/queries/match_query.rb +11 -0
  28. data/lib/capybara/queries/selector_query.rb +617 -104
  29. data/lib/capybara/queries/sibling_query.rb +9 -7
  30. data/lib/capybara/queries/style_query.rb +45 -0
  31. data/lib/capybara/queries/text_query.rb +40 -22
  32. data/lib/capybara/queries/title_query.rb +2 -2
  33. data/lib/capybara/rack_test/browser.rb +106 -31
  34. data/lib/capybara/rack_test/driver.rb +16 -7
  35. data/lib/capybara/rack_test/errors.rb +6 -0
  36. data/lib/capybara/rack_test/form.rb +74 -49
  37. data/lib/capybara/rack_test/node.rb +120 -47
  38. data/lib/capybara/rails.rb +1 -1
  39. data/lib/capybara/registration_container.rb +41 -0
  40. data/lib/capybara/registrations/drivers.rb +42 -0
  41. data/lib/capybara/registrations/patches/puma_ssl.rb +29 -0
  42. data/lib/capybara/registrations/servers.rb +66 -0
  43. data/lib/capybara/result.rb +87 -53
  44. data/lib/capybara/rspec/features.rb +8 -10
  45. data/lib/capybara/rspec/matcher_proxies.rb +39 -18
  46. data/lib/capybara/rspec/matchers/base.rb +113 -0
  47. data/lib/capybara/rspec/matchers/become_closed.rb +33 -0
  48. data/lib/capybara/rspec/matchers/compound.rb +88 -0
  49. data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
  50. data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
  51. data/lib/capybara/rspec/matchers/have_current_path.rb +29 -0
  52. data/lib/capybara/rspec/matchers/have_selector.rb +69 -0
  53. data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
  54. data/lib/capybara/rspec/matchers/have_text.rb +33 -0
  55. data/lib/capybara/rspec/matchers/have_title.rb +29 -0
  56. data/lib/capybara/rspec/matchers/match_selector.rb +27 -0
  57. data/lib/capybara/rspec/matchers/match_style.rb +43 -0
  58. data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
  59. data/lib/capybara/rspec/matchers.rb +142 -315
  60. data/lib/capybara/rspec.rb +3 -2
  61. data/lib/capybara/selector/builders/css_builder.rb +84 -0
  62. data/lib/capybara/selector/builders/xpath_builder.rb +71 -0
  63. data/lib/capybara/selector/css.rb +85 -8
  64. data/lib/capybara/selector/definition/button.rb +68 -0
  65. data/lib/capybara/selector/definition/checkbox.rb +26 -0
  66. data/lib/capybara/selector/definition/css.rb +10 -0
  67. data/lib/capybara/selector/definition/datalist_input.rb +35 -0
  68. data/lib/capybara/selector/definition/datalist_option.rb +25 -0
  69. data/lib/capybara/selector/definition/element.rb +28 -0
  70. data/lib/capybara/selector/definition/field.rb +40 -0
  71. data/lib/capybara/selector/definition/fieldset.rb +14 -0
  72. data/lib/capybara/selector/definition/file_field.rb +13 -0
  73. data/lib/capybara/selector/definition/fillable_field.rb +33 -0
  74. data/lib/capybara/selector/definition/frame.rb +17 -0
  75. data/lib/capybara/selector/definition/id.rb +6 -0
  76. data/lib/capybara/selector/definition/label.rb +62 -0
  77. data/lib/capybara/selector/definition/link.rb +55 -0
  78. data/lib/capybara/selector/definition/link_or_button.rb +16 -0
  79. data/lib/capybara/selector/definition/option.rb +27 -0
  80. data/lib/capybara/selector/definition/radio_button.rb +27 -0
  81. data/lib/capybara/selector/definition/select.rb +81 -0
  82. data/lib/capybara/selector/definition/table.rb +109 -0
  83. data/lib/capybara/selector/definition/table_row.rb +21 -0
  84. data/lib/capybara/selector/definition/xpath.rb +5 -0
  85. data/lib/capybara/selector/definition.rb +280 -0
  86. data/lib/capybara/selector/filter.rb +1 -0
  87. data/lib/capybara/selector/filter_set.rb +72 -28
  88. data/lib/capybara/selector/filters/base.rb +45 -2
  89. data/lib/capybara/selector/filters/expression_filter.rb +5 -6
  90. data/lib/capybara/selector/filters/locator_filter.rb +29 -0
  91. data/lib/capybara/selector/filters/node_filter.rb +18 -4
  92. data/lib/capybara/selector/regexp_disassembler.rb +211 -0
  93. data/lib/capybara/selector/selector.rb +89 -200
  94. data/lib/capybara/selector/xpath_extensions.rb +17 -0
  95. data/lib/capybara/selector.rb +474 -534
  96. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -0
  97. data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -0
  98. data/lib/capybara/selenium/atoms/src/getAttribute.js +161 -0
  99. data/lib/capybara/selenium/atoms/src/isDisplayed.js +454 -0
  100. data/lib/capybara/selenium/driver.rb +270 -245
  101. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +117 -0
  102. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +128 -0
  103. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +84 -0
  104. data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +26 -0
  105. data/lib/capybara/selenium/driver_specializations/safari_driver.rb +24 -0
  106. data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
  107. data/lib/capybara/selenium/extensions/find.rb +110 -0
  108. data/lib/capybara/selenium/extensions/html5_drag.rb +229 -0
  109. data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
  110. data/lib/capybara/selenium/extensions/scroll.rb +76 -0
  111. data/lib/capybara/selenium/node.rb +460 -170
  112. data/lib/capybara/selenium/nodes/chrome_node.rb +125 -0
  113. data/lib/capybara/selenium/nodes/edge_node.rb +110 -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/atoms.rb +18 -0
  118. data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
  119. data/lib/capybara/selenium/patches/logs.rb +45 -0
  120. data/lib/capybara/selenium/patches/pause_duration_fix.rb +9 -0
  121. data/lib/capybara/selenium/patches/persistent_client.rb +20 -0
  122. data/lib/capybara/server/animation_disabler.rb +80 -0
  123. data/lib/capybara/server/checker.rb +44 -0
  124. data/lib/capybara/server/middleware.rb +71 -0
  125. data/lib/capybara/server.rb +58 -67
  126. data/lib/capybara/session/config.rb +40 -6
  127. data/lib/capybara/session/matchers.rb +26 -19
  128. data/lib/capybara/session.rb +252 -194
  129. data/lib/capybara/spec/public/jquery.js +5 -5
  130. data/lib/capybara/spec/public/offset.js +6 -0
  131. data/lib/capybara/spec/public/test.js +126 -8
  132. data/lib/capybara/spec/session/accept_alert_spec.rb +11 -11
  133. data/lib/capybara/spec/session/accept_confirm_spec.rb +3 -3
  134. data/lib/capybara/spec/session/accept_prompt_spec.rb +9 -10
  135. data/lib/capybara/spec/session/active_element_spec.rb +31 -0
  136. data/lib/capybara/spec/session/all_spec.rb +135 -44
  137. data/lib/capybara/spec/session/ancestor_spec.rb +24 -19
  138. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +67 -38
  139. data/lib/capybara/spec/session/{assert_current_path.rb → assert_current_path_spec.rb} +20 -18
  140. data/lib/capybara/spec/session/assert_selector_spec.rb +143 -0
  141. data/lib/capybara/spec/session/assert_style_spec.rb +26 -0
  142. data/lib/capybara/spec/session/{assert_text.rb → assert_text_spec.rb} +76 -52
  143. data/lib/capybara/spec/session/{assert_title.rb → assert_title_spec.rb} +12 -12
  144. data/lib/capybara/spec/session/attach_file_spec.rb +126 -72
  145. data/lib/capybara/spec/session/body_spec.rb +11 -13
  146. data/lib/capybara/spec/session/check_spec.rb +112 -51
  147. data/lib/capybara/spec/session/choose_spec.rb +62 -30
  148. data/lib/capybara/spec/session/click_button_spec.rb +227 -161
  149. data/lib/capybara/spec/session/click_link_or_button_spec.rb +49 -30
  150. data/lib/capybara/spec/session/click_link_spec.rb +89 -55
  151. data/lib/capybara/spec/session/current_scope_spec.rb +8 -8
  152. data/lib/capybara/spec/session/current_url_spec.rb +44 -37
  153. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +3 -3
  154. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +2 -2
  155. data/lib/capybara/spec/session/element/{assert_match_selector.rb → assert_match_selector_spec.rb} +11 -9
  156. data/lib/capybara/spec/session/element/match_css_spec.rb +18 -10
  157. data/lib/capybara/spec/session/element/match_xpath_spec.rb +8 -6
  158. data/lib/capybara/spec/session/element/matches_selector_spec.rb +70 -56
  159. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +7 -7
  160. data/lib/capybara/spec/session/evaluate_script_spec.rb +28 -8
  161. data/lib/capybara/spec/session/execute_script_spec.rb +8 -7
  162. data/lib/capybara/spec/session/fill_in_spec.rb +110 -44
  163. data/lib/capybara/spec/session/find_button_spec.rb +23 -23
  164. data/lib/capybara/spec/session/find_by_id_spec.rb +8 -8
  165. data/lib/capybara/spec/session/find_field_spec.rb +33 -31
  166. data/lib/capybara/spec/session/find_link_spec.rb +42 -14
  167. data/lib/capybara/spec/session/find_spec.rb +251 -142
  168. data/lib/capybara/spec/session/first_spec.rb +45 -44
  169. data/lib/capybara/spec/session/frame/frame_title_spec.rb +6 -6
  170. data/lib/capybara/spec/session/frame/frame_url_spec.rb +6 -6
  171. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +32 -20
  172. data/lib/capybara/spec/session/frame/within_frame_spec.rb +46 -19
  173. data/lib/capybara/spec/session/go_back_spec.rb +1 -1
  174. data/lib/capybara/spec/session/go_forward_spec.rb +1 -1
  175. data/lib/capybara/spec/session/has_all_selectors_spec.rb +23 -23
  176. data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
  177. data/lib/capybara/spec/session/has_any_selectors_spec.rb +29 -0
  178. data/lib/capybara/spec/session/has_button_spec.rb +98 -12
  179. data/lib/capybara/spec/session/has_css_spec.rb +271 -137
  180. data/lib/capybara/spec/session/has_current_path_spec.rb +50 -35
  181. data/lib/capybara/spec/session/has_element_spec.rb +47 -0
  182. data/lib/capybara/spec/session/has_field_spec.rb +137 -58
  183. data/lib/capybara/spec/session/has_link_spec.rb +46 -6
  184. data/lib/capybara/spec/session/has_none_selectors_spec.rb +33 -31
  185. data/lib/capybara/spec/session/has_select_spec.rb +84 -50
  186. data/lib/capybara/spec/session/has_selector_spec.rb +117 -69
  187. data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
  188. data/lib/capybara/spec/session/has_table_spec.rb +181 -4
  189. data/lib/capybara/spec/session/has_text_spec.rb +101 -53
  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 +37 -0
  195. data/lib/capybara/spec/session/node_spec.rb +958 -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 +65 -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 +119 -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 +41 -22
  212. data/lib/capybara/spec/session/unselect_spec.rb +37 -37
  213. data/lib/capybara/spec/session/visit_spec.rb +79 -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 +11 -8
  221. data/lib/capybara/spec/session/window/within_window_spec.rb +17 -16
  222. data/lib/capybara/spec/session/within_spec.rb +82 -44
  223. data/lib/capybara/spec/spec_helper.rb +46 -52
  224. data/lib/capybara/spec/test_app.rb +148 -41
  225. data/lib/capybara/spec/views/animated.erb +49 -0
  226. data/lib/capybara/spec/views/form.erb +156 -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 +46 -11
  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.erb +2 -2
  255. data/lib/capybara/spec/views/with_scope_other.erb +6 -0
  256. data/lib/capybara/spec/views/with_shadow.erb +31 -0
  257. data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
  258. data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
  259. data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
  260. data/lib/capybara/spec/views/with_windows.erb +1 -1
  261. data/lib/capybara/spec/views/within_frames.erb +1 -1
  262. data/lib/capybara/version.rb +1 -1
  263. data/lib/capybara/window.rb +32 -26
  264. data/lib/capybara.rb +128 -104
  265. data/spec/basic_node_spec.rb +59 -34
  266. data/spec/capybara_spec.rb +65 -51
  267. data/spec/counter_spec.rb +35 -0
  268. data/spec/css_builder_spec.rb +101 -0
  269. data/spec/css_splitter_spec.rb +38 -0
  270. data/spec/dsl_spec.rb +84 -55
  271. data/spec/filter_set_spec.rb +24 -7
  272. data/spec/fixtures/certificate.pem +25 -0
  273. data/spec/fixtures/key.pem +27 -0
  274. data/spec/fixtures/selenium_driver_rspec_failure.rb +5 -5
  275. data/spec/fixtures/selenium_driver_rspec_success.rb +5 -5
  276. data/spec/minitest_spec.rb +49 -5
  277. data/spec/minitest_spec_spec.rb +92 -62
  278. data/spec/per_session_config_spec.rb +6 -6
  279. data/spec/rack_test_spec.rb +183 -115
  280. data/spec/regexp_dissassembler_spec.rb +250 -0
  281. data/spec/result_spec.rb +99 -39
  282. data/spec/rspec/features_spec.rb +28 -25
  283. data/spec/rspec/scenarios_spec.rb +10 -6
  284. data/spec/rspec/shared_spec_matchers.rb +418 -364
  285. data/spec/rspec/views_spec.rb +4 -3
  286. data/spec/rspec_matchers_spec.rb +35 -10
  287. data/spec/rspec_spec.rb +109 -85
  288. data/spec/sauce_spec_chrome.rb +43 -0
  289. data/spec/selector_spec.rb +392 -62
  290. data/spec/selenium_spec_chrome.rb +183 -41
  291. data/spec/selenium_spec_chrome_remote.rb +96 -0
  292. data/spec/selenium_spec_edge.rb +41 -8
  293. data/spec/selenium_spec_firefox.rb +228 -0
  294. data/spec/selenium_spec_firefox_remote.rb +94 -0
  295. data/spec/selenium_spec_ie.rb +129 -11
  296. data/spec/selenium_spec_safari.rb +162 -0
  297. data/spec/server_spec.rb +192 -81
  298. data/spec/session_spec.rb +52 -16
  299. data/spec/shared_selenium_node.rb +79 -0
  300. data/spec/shared_selenium_session.rb +460 -123
  301. data/spec/spec_helper.rb +124 -2
  302. data/spec/whitespace_normalizer_spec.rb +54 -0
  303. data/spec/xpath_builder_spec.rb +93 -0
  304. metadata +344 -45
  305. data/.yard/templates_custom/default/class/html/selectors.erb +0 -38
  306. data/.yard/templates_custom/default/class/html/setup.rb +0 -17
  307. data/.yard/yard_extensions.rb +0 -78
  308. data/lib/capybara/rspec/compound.rb +0 -90
  309. data/lib/capybara/spec/session/assert_selector.rb +0 -149
  310. data/lib/capybara/spec/session/source_spec.rb +0 -0
  311. data/lib/capybara/spec/views/with_title.erb +0 -5
  312. data/spec/selenium_spec_marionette.rb +0 -143
@@ -1,229 +1,566 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'spec_helper'
4
- require "selenium-webdriver"
4
+ require 'selenium-webdriver'
5
5
 
6
- RSpec.shared_examples "Capybara::Session" do |session, mode|
6
+ RSpec.shared_examples 'Capybara::Session' do |session, mode|
7
7
  let(:session) { session }
8
8
 
9
9
  context 'with selenium driver' do
10
- before do
11
- @session = session
12
- end
13
-
14
10
  describe '#driver' do
15
- it "should be a selenium driver" do
16
- 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)
17
13
  end
18
14
  end
19
15
 
20
16
  describe '#mode' do
21
- it "should remember the mode" do
22
- expect(@session.mode).to eq(mode)
17
+ it 'should remember the mode' do
18
+ expect(session.mode).to eq(mode)
23
19
  end
24
20
  end
25
21
 
26
- describe "#reset!" do
27
- it "freshly reset session should not be touched" do
28
- @session.instance_variable_set(:@touched, true)
29
- @session.reset!
30
- 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
31
27
  end
32
28
  end
33
29
 
34
- 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
+
35
34
  before do
36
- @current_dir = Dir.getwd
37
35
  Dir.chdir(File.join(File.dirname(__FILE__), '..'))
38
- @env = { 'SELENIUM_BROWSER' => @session.driver.options[:browser].to_s }
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)
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)
48
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)
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)
54
55
  expect($CHILD_STATUS.exitstatus).to eq(0)
55
56
  end
56
57
  end
57
58
 
58
- describe "#accept_alert", requires: [:modals] 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',
88
- with: 'Harry',
89
- fill_options: { clear: :backspace })
90
- 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')
91
117
  end
92
118
 
93
119
  it 'should only trigger onchange once' do
94
- @session.visit('/with_js')
95
- @session.fill_in('with_change_event',
96
- with: 'some value',
97
- 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 })
98
125
  # click outside the field to trigger the change event
99
- @session.find(:css, 'body').click
100
- 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'
101
128
  end
102
129
 
103
130
  it 'should trigger change when clearing field' do
104
- @session.visit('/with_js')
105
- @session.fill_in('with_change_event',
106
- with: '',
107
- fill_options: { clear: :backspace })
131
+ session.visit('/with_js')
132
+ session.fill_in('with_change_event',
133
+ with: '',
134
+ fill_options: { clear: :backspace })
108
135
  # click outside the field to trigger the change event
109
- @session.find(:css, 'body').click
110
- 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)
111
138
  end
112
139
 
113
140
  it 'should trigger input event field_value.length times' do
114
- @session.visit('/with_js')
115
- @session.fill_in('with_change_event',
116
- with: '',
117
- fill_options: { clear: :backspace })
141
+ session.visit('/with_js')
142
+ session.fill_in('with_change_event',
143
+ with: '',
144
+ fill_options: { clear: :backspace })
118
145
  # click outside the field to trigger the change event
119
- @session.find(:css, 'body').click
120
- 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)
121
149
  end
122
150
  end
123
151
 
124
- context "#fill_in with { clear: :none } fill_options" do
152
+ describe '#fill_in with { clear: :none } fill_options' do
125
153
  it 'should append to content in a field' do
126
- @session.visit('/form')
127
- @session.fill_in('form_first_name',
128
- with: 'Harry',
129
- fill_options: { clear: :none })
130
- 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}")
131
170
  end
132
171
  end
133
172
 
134
- context "#fill_in with { clear: Array } fill_options" do
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])
200
+ end
201
+ end
202
+
203
+ describe '#fill_in with { clear: Array } fill_options' do
135
204
  it 'should pass the array through to the element' do
136
- pending "selenium-webdriver/geckodriver doesn't support complex sets of characters" if marionette?(@session)
137
205
  # this is mainly for use with [[:control, 'a'], :backspace] - however since that is platform dependant I'm testing with something less useful
138
- @session.visit('/form')
139
- @session.fill_in('form_first_name',
140
- with: 'Harry',
141
- fill_options: { clear: [[:shift, 'abc'], :backspace] })
142
- expect(@session.find(:fillable_field, 'form_first_name').value).to eq('JohnABHarry')
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"]')
143
231
  end
144
232
  end
145
233
 
146
- describe "#path" do
147
- it "returns xpath" do
234
+ describe '#path' do
235
+ it 'returns xpath' do
148
236
  # this is here because it is testing for an XPath that is specific to the algorithm used in the selenium driver
149
- @session.visit('/path')
150
- element = @session.find(:link, 'Second Link')
151
- 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
152
265
  end
153
266
  end
154
267
 
155
- describe "all with disappearing elements" do
156
- it "ignores stale elements in results" do
157
- @session.visit('/path')
158
- 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 }
159
272
  expect(elements.size).to eq 0
160
273
  end
161
274
  end
162
275
 
163
- describe "#evaluate_script" do
164
- it "can return an element" do
165
- @session.visit('/form')
166
- element = @session.evaluate_script("document.getElementById('form_title')")
167
- 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')
168
281
  end
169
282
 
170
- it "can return arrays of nested elements" do
171
- @session.visit('/form')
172
- elements = @session.evaluate_script('document.querySelectorAll("#form_city option")')
173
- elements.each do |el|
174
- expect(el).to be_instance_of Capybara::Node::Element
175
- end
176
- 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)
177
288
  end
178
289
 
179
- it "can return hashes with elements" do
180
- @session.visit('/form')
181
- result = @session.evaluate_script("{ a: document.getElementById('form_title'), b: {c: document.querySelectorAll('#form_city option')}}")
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')}}")
182
300
  expect(result).to eq(
183
- 'a' => @session.find(:id, 'form_title'),
301
+ 'a' => session.find(:id, 'form_title'),
184
302
  'b' => {
185
- 'c' => @session.find(:css, '#form_city').all(:css, 'option').to_a
303
+ 'c' => session.find(:css, '#form_city').all(:css, 'option').to_a
186
304
  }
187
305
  )
188
306
  end
189
307
 
190
- describe "#evaluate_async_script" do
191
- it "will timeout if the script takes too long" do
192
- @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')
193
312
  expect do
194
- @session.using_wait_time(1) do
195
- @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)')
196
315
  end
197
316
  end.to raise_error Selenium::WebDriver::Error::ScriptTimeoutError
198
317
  end
199
318
  end
200
319
  end
201
320
 
202
- describe "Element#inspect" do
203
- it "outputs obsolete elements" do
204
- @session.visit('/form')
205
- el = @session.find(:button, 'Click me!').click
206
- expect(@session).to have_no_button('Click me!')
207
- expect(el).not_to receive(:synchronize)
208
- 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)
209
329
  end
210
330
  end
211
331
 
212
- describe "Element#click" do
213
- it "should handle fixed headers/footers" do
214
- @session.visit('/with_fixed_header_footer')
215
- # @session.click_link('Go to root')
216
- @session.find(:link, 'Go to root').click
217
- expect(@session).to have_current_path('/')
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('/')
218
339
  end
219
340
  end
220
341
 
221
- context "Windows" do
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')
364
+ end
365
+ end
366
+
367
+ context 'Windows' do
222
368
  it "can't close the primary window" do
223
369
  expect do
224
- @session.current_window.close
370
+ session.current_window.close
225
371
  end.to raise_error(ArgumentError, 'Not allowed to close the primary window')
226
372
  end
227
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
228
565
  end
229
566
  end