capybara 2.7.0 → 3.35.3

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 (318) hide show
  1. checksums.yaml +5 -5
  2. data/.yardopts +1 -0
  3. data/History.md +1147 -11
  4. data/License.txt +1 -1
  5. data/README.md +252 -131
  6. data/lib/capybara/config.rb +92 -0
  7. data/lib/capybara/cucumber.rb +3 -3
  8. data/lib/capybara/driver/base.rb +52 -21
  9. data/lib/capybara/driver/node.rb +48 -14
  10. data/lib/capybara/dsl.rb +16 -9
  11. data/lib/capybara/helpers.rb +72 -81
  12. data/lib/capybara/minitest/spec.rb +267 -0
  13. data/lib/capybara/minitest.rb +385 -0
  14. data/lib/capybara/node/actions.rb +337 -89
  15. data/lib/capybara/node/base.rb +50 -32
  16. data/lib/capybara/node/document.rb +19 -3
  17. data/lib/capybara/node/document_matchers.rb +22 -24
  18. data/lib/capybara/node/element.rb +388 -125
  19. data/lib/capybara/node/finders.rb +231 -121
  20. data/lib/capybara/node/matchers.rb +503 -217
  21. data/lib/capybara/node/simple.rb +64 -27
  22. data/lib/capybara/queries/ancestor_query.rb +27 -0
  23. data/lib/capybara/queries/base_query.rb +87 -11
  24. data/lib/capybara/queries/current_path_query.rb +24 -24
  25. data/lib/capybara/queries/match_query.rb +15 -10
  26. data/lib/capybara/queries/selector_query.rb +675 -81
  27. data/lib/capybara/queries/sibling_query.rb +26 -0
  28. data/lib/capybara/queries/style_query.rb +45 -0
  29. data/lib/capybara/queries/text_query.rb +88 -20
  30. data/lib/capybara/queries/title_query.rb +9 -11
  31. data/lib/capybara/rack_test/browser.rb +63 -39
  32. data/lib/capybara/rack_test/css_handlers.rb +6 -4
  33. data/lib/capybara/rack_test/driver.rb +26 -16
  34. data/lib/capybara/rack_test/errors.rb +6 -0
  35. data/lib/capybara/rack_test/form.rb +73 -58
  36. data/lib/capybara/rack_test/node.rb +187 -67
  37. data/lib/capybara/rails.rb +4 -8
  38. data/lib/capybara/registration_container.rb +44 -0
  39. data/lib/capybara/registrations/drivers.rb +42 -0
  40. data/lib/capybara/registrations/patches/puma_ssl.rb +29 -0
  41. data/lib/capybara/registrations/servers.rb +45 -0
  42. data/lib/capybara/result.rb +142 -14
  43. data/lib/capybara/rspec/features.rb +17 -42
  44. data/lib/capybara/rspec/matcher_proxies.rb +82 -0
  45. data/lib/capybara/rspec/matchers/base.rb +111 -0
  46. data/lib/capybara/rspec/matchers/become_closed.rb +33 -0
  47. data/lib/capybara/rspec/matchers/compound.rb +88 -0
  48. data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
  49. data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
  50. data/lib/capybara/rspec/matchers/have_current_path.rb +29 -0
  51. data/lib/capybara/rspec/matchers/have_selector.rb +77 -0
  52. data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
  53. data/lib/capybara/rspec/matchers/have_text.rb +33 -0
  54. data/lib/capybara/rspec/matchers/have_title.rb +29 -0
  55. data/lib/capybara/rspec/matchers/match_selector.rb +27 -0
  56. data/lib/capybara/rspec/matchers/match_style.rb +43 -0
  57. data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
  58. data/lib/capybara/rspec/matchers.rb +143 -244
  59. data/lib/capybara/rspec.rb +10 -12
  60. data/lib/capybara/selector/builders/css_builder.rb +84 -0
  61. data/lib/capybara/selector/builders/xpath_builder.rb +71 -0
  62. data/lib/capybara/selector/css.rb +102 -0
  63. data/lib/capybara/selector/definition/button.rb +63 -0
  64. data/lib/capybara/selector/definition/checkbox.rb +26 -0
  65. data/lib/capybara/selector/definition/css.rb +10 -0
  66. data/lib/capybara/selector/definition/datalist_input.rb +35 -0
  67. data/lib/capybara/selector/definition/datalist_option.rb +25 -0
  68. data/lib/capybara/selector/definition/element.rb +28 -0
  69. data/lib/capybara/selector/definition/field.rb +40 -0
  70. data/lib/capybara/selector/definition/fieldset.rb +14 -0
  71. data/lib/capybara/selector/definition/file_field.rb +13 -0
  72. data/lib/capybara/selector/definition/fillable_field.rb +33 -0
  73. data/lib/capybara/selector/definition/frame.rb +17 -0
  74. data/lib/capybara/selector/definition/id.rb +6 -0
  75. data/lib/capybara/selector/definition/label.rb +62 -0
  76. data/lib/capybara/selector/definition/link.rb +54 -0
  77. data/lib/capybara/selector/definition/link_or_button.rb +16 -0
  78. data/lib/capybara/selector/definition/option.rb +27 -0
  79. data/lib/capybara/selector/definition/radio_button.rb +27 -0
  80. data/lib/capybara/selector/definition/select.rb +81 -0
  81. data/lib/capybara/selector/definition/table.rb +109 -0
  82. data/lib/capybara/selector/definition/table_row.rb +21 -0
  83. data/lib/capybara/selector/definition/xpath.rb +5 -0
  84. data/lib/capybara/selector/definition.rb +278 -0
  85. data/lib/capybara/selector/filter.rb +3 -46
  86. data/lib/capybara/selector/filter_set.rb +124 -0
  87. data/lib/capybara/selector/filters/base.rb +77 -0
  88. data/lib/capybara/selector/filters/expression_filter.rb +22 -0
  89. data/lib/capybara/selector/filters/locator_filter.rb +29 -0
  90. data/lib/capybara/selector/filters/node_filter.rb +31 -0
  91. data/lib/capybara/selector/regexp_disassembler.rb +214 -0
  92. data/lib/capybara/selector/selector.rb +155 -0
  93. data/lib/capybara/selector/xpath_extensions.rb +17 -0
  94. data/lib/capybara/selector.rb +232 -369
  95. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -0
  96. data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -0
  97. data/lib/capybara/selenium/atoms/src/getAttribute.js +161 -0
  98. data/lib/capybara/selenium/atoms/src/isDisplayed.js +454 -0
  99. data/lib/capybara/selenium/driver.rb +380 -142
  100. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +117 -0
  101. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +124 -0
  102. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +89 -0
  103. data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +26 -0
  104. data/lib/capybara/selenium/driver_specializations/safari_driver.rb +24 -0
  105. data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
  106. data/lib/capybara/selenium/extensions/find.rb +110 -0
  107. data/lib/capybara/selenium/extensions/html5_drag.rb +228 -0
  108. data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
  109. data/lib/capybara/selenium/extensions/scroll.rb +76 -0
  110. data/lib/capybara/selenium/logger_suppressor.rb +40 -0
  111. data/lib/capybara/selenium/node.rb +528 -97
  112. data/lib/capybara/selenium/nodes/chrome_node.rb +137 -0
  113. data/lib/capybara/selenium/nodes/edge_node.rb +104 -0
  114. data/lib/capybara/selenium/nodes/firefox_node.rb +136 -0
  115. data/lib/capybara/selenium/nodes/ie_node.rb +22 -0
  116. data/lib/capybara/selenium/nodes/safari_node.rb +118 -0
  117. data/lib/capybara/selenium/patches/action_pauser.rb +26 -0
  118. data/lib/capybara/selenium/patches/atoms.rb +18 -0
  119. data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
  120. data/lib/capybara/selenium/patches/logs.rb +45 -0
  121. data/lib/capybara/selenium/patches/pause_duration_fix.rb +9 -0
  122. data/lib/capybara/selenium/patches/persistent_client.rb +20 -0
  123. data/lib/capybara/server/animation_disabler.rb +63 -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 +74 -71
  127. data/lib/capybara/session/config.rb +126 -0
  128. data/lib/capybara/session/matchers.rb +44 -27
  129. data/lib/capybara/session.rb +500 -297
  130. data/lib/capybara/spec/fixtures/no_extension +1 -0
  131. data/lib/capybara/spec/public/jquery.js +5 -5
  132. data/lib/capybara/spec/public/offset.js +6 -0
  133. data/lib/capybara/spec/public/test.js +168 -14
  134. data/lib/capybara/spec/session/accept_alert_spec.rb +37 -14
  135. data/lib/capybara/spec/session/accept_confirm_spec.rb +7 -6
  136. data/lib/capybara/spec/session/accept_prompt_spec.rb +38 -10
  137. data/lib/capybara/spec/session/all_spec.rb +179 -59
  138. data/lib/capybara/spec/session/ancestor_spec.rb +88 -0
  139. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +140 -0
  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_spec.rb +93 -0
  145. data/lib/capybara/spec/session/attach_file_spec.rb +154 -48
  146. data/lib/capybara/spec/session/body_spec.rb +12 -13
  147. data/lib/capybara/spec/session/check_spec.rb +168 -41
  148. data/lib/capybara/spec/session/choose_spec.rb +75 -23
  149. data/lib/capybara/spec/session/click_button_spec.rb +243 -175
  150. data/lib/capybara/spec/session/click_link_or_button_spec.rb +57 -32
  151. data/lib/capybara/spec/session/click_link_spec.rb +100 -53
  152. data/lib/capybara/spec/session/current_scope_spec.rb +11 -10
  153. data/lib/capybara/spec/session/current_url_spec.rb +61 -35
  154. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +7 -7
  155. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +5 -4
  156. data/lib/capybara/spec/session/element/{assert_match_selector.rb → assert_match_selector_spec.rb} +13 -6
  157. data/lib/capybara/spec/session/element/match_css_spec.rb +21 -7
  158. data/lib/capybara/spec/session/element/match_xpath_spec.rb +9 -7
  159. data/lib/capybara/spec/session/element/matches_selector_spec.rb +91 -34
  160. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +23 -0
  161. data/lib/capybara/spec/session/evaluate_script_spec.rb +45 -3
  162. data/lib/capybara/spec/session/execute_script_spec.rb +24 -4
  163. data/lib/capybara/spec/session/fill_in_spec.rb +166 -64
  164. data/lib/capybara/spec/session/find_button_spec.rb +37 -18
  165. data/lib/capybara/spec/session/find_by_id_spec.rb +10 -9
  166. data/lib/capybara/spec/session/find_field_spec.rb +57 -34
  167. data/lib/capybara/spec/session/find_link_spec.rb +47 -10
  168. data/lib/capybara/spec/session/find_spec.rb +290 -144
  169. data/lib/capybara/spec/session/first_spec.rb +91 -48
  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 +116 -0
  173. data/lib/capybara/spec/session/frame/within_frame_spec.rb +112 -0
  174. data/lib/capybara/spec/session/go_back_spec.rb +3 -2
  175. data/lib/capybara/spec/session/go_forward_spec.rb +3 -2
  176. data/lib/capybara/spec/session/has_all_selectors_spec.rb +69 -0
  177. data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
  178. data/lib/capybara/spec/session/has_any_selectors_spec.rb +25 -0
  179. data/lib/capybara/spec/session/has_button_spec.rb +76 -19
  180. data/lib/capybara/spec/session/has_css_spec.rb +277 -131
  181. data/lib/capybara/spec/session/has_current_path_spec.rb +98 -26
  182. data/lib/capybara/spec/session/has_field_spec.rb +177 -107
  183. data/lib/capybara/spec/session/has_link_spec.rb +13 -12
  184. data/lib/capybara/spec/session/has_none_selectors_spec.rb +78 -0
  185. data/lib/capybara/spec/session/has_select_spec.rb +191 -95
  186. data/lib/capybara/spec/session/has_selector_spec.rb +128 -64
  187. data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
  188. data/lib/capybara/spec/session/has_table_spec.rb +172 -5
  189. data/lib/capybara/spec/session/has_text_spec.rb +126 -60
  190. data/lib/capybara/spec/session/has_title_spec.rb +35 -12
  191. data/lib/capybara/spec/session/has_xpath_spec.rb +74 -53
  192. data/lib/capybara/spec/session/{headers.rb → headers_spec.rb} +3 -2
  193. data/lib/capybara/spec/session/html_spec.rb +14 -6
  194. data/lib/capybara/spec/session/matches_style_spec.rb +35 -0
  195. data/lib/capybara/spec/session/node_spec.rb +1028 -131
  196. data/lib/capybara/spec/session/node_wrapper_spec.rb +39 -0
  197. data/lib/capybara/spec/session/refresh_spec.rb +34 -0
  198. data/lib/capybara/spec/session/reset_session_spec.rb +75 -34
  199. data/lib/capybara/spec/session/{response_code.rb → response_code_spec.rb} +2 -1
  200. data/lib/capybara/spec/session/save_and_open_page_spec.rb +3 -2
  201. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +11 -15
  202. data/lib/capybara/spec/session/save_page_spec.rb +42 -55
  203. data/lib/capybara/spec/session/save_screenshot_spec.rb +16 -14
  204. data/lib/capybara/spec/session/screenshot_spec.rb +2 -2
  205. data/lib/capybara/spec/session/scroll_spec.rb +117 -0
  206. data/lib/capybara/spec/session/select_spec.rb +112 -85
  207. data/lib/capybara/spec/session/selectors_spec.rb +71 -8
  208. data/lib/capybara/spec/session/sibling_spec.rb +52 -0
  209. data/lib/capybara/spec/session/text_spec.rb +38 -23
  210. data/lib/capybara/spec/session/title_spec.rb +17 -5
  211. data/lib/capybara/spec/session/uncheck_spec.rb +71 -12
  212. data/lib/capybara/spec/session/unselect_spec.rb +44 -43
  213. data/lib/capybara/spec/session/visit_spec.rb +99 -32
  214. data/lib/capybara/spec/session/window/become_closed_spec.rb +33 -29
  215. data/lib/capybara/spec/session/window/current_window_spec.rb +5 -3
  216. data/lib/capybara/spec/session/window/open_new_window_spec.rb +5 -3
  217. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +39 -30
  218. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +17 -10
  219. data/lib/capybara/spec/session/window/window_spec.rb +121 -73
  220. data/lib/capybara/spec/session/window/windows_spec.rb +12 -10
  221. data/lib/capybara/spec/session/window/within_window_spec.rb +52 -82
  222. data/lib/capybara/spec/session/within_spec.rb +76 -43
  223. data/lib/capybara/spec/spec_helper.rb +67 -33
  224. data/lib/capybara/spec/test_app.rb +85 -36
  225. data/lib/capybara/spec/views/animated.erb +49 -0
  226. data/lib/capybara/spec/views/buttons.erb +1 -1
  227. data/lib/capybara/spec/views/fieldsets.erb +1 -1
  228. data/lib/capybara/spec/views/form.erb +227 -20
  229. data/lib/capybara/spec/views/frame_child.erb +10 -2
  230. data/lib/capybara/spec/views/frame_one.erb +2 -1
  231. data/lib/capybara/spec/views/frame_parent.erb +2 -2
  232. data/lib/capybara/spec/views/frame_two.erb +1 -1
  233. data/lib/capybara/spec/views/header_links.erb +1 -1
  234. data/lib/capybara/spec/views/host_links.erb +1 -1
  235. data/lib/capybara/spec/views/initial_alert.erb +10 -0
  236. data/lib/capybara/spec/views/obscured.erb +47 -0
  237. data/lib/capybara/spec/views/offset.erb +32 -0
  238. data/lib/capybara/spec/views/path.erb +1 -1
  239. data/lib/capybara/spec/views/popup_one.erb +1 -1
  240. data/lib/capybara/spec/views/popup_two.erb +1 -1
  241. data/lib/capybara/spec/views/postback.erb +1 -1
  242. data/lib/capybara/spec/views/react.erb +45 -0
  243. data/lib/capybara/spec/views/scroll.erb +20 -0
  244. data/lib/capybara/spec/views/spatial.erb +31 -0
  245. data/lib/capybara/spec/views/tables.erb +69 -2
  246. data/lib/capybara/spec/views/with_animation.erb +82 -0
  247. data/lib/capybara/spec/views/with_base_tag.erb +1 -1
  248. data/lib/capybara/spec/views/with_count.erb +1 -1
  249. data/lib/capybara/spec/views/with_dragula.erb +24 -0
  250. data/lib/capybara/spec/views/with_fixed_header_footer.erb +17 -0
  251. data/lib/capybara/spec/views/with_hover.erb +7 -1
  252. data/lib/capybara/spec/views/with_hover1.erb +10 -0
  253. data/lib/capybara/spec/views/with_html.erb +100 -10
  254. data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
  255. data/lib/capybara/spec/views/with_html_entities.erb +1 -1
  256. data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
  257. data/lib/capybara/spec/views/with_js.erb +49 -3
  258. data/lib/capybara/spec/views/with_jstree.erb +26 -0
  259. data/lib/capybara/spec/views/with_namespace.erb +20 -0
  260. data/lib/capybara/spec/views/with_scope.erb +1 -1
  261. data/lib/capybara/spec/views/with_scope_other.erb +6 -0
  262. data/lib/capybara/spec/views/with_simple_html.erb +1 -1
  263. data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
  264. data/lib/capybara/spec/views/with_title.erb +1 -1
  265. data/lib/capybara/spec/views/with_unload_alert.erb +3 -1
  266. data/lib/capybara/spec/views/with_windows.erb +7 -1
  267. data/lib/capybara/spec/views/within_frames.erb +6 -3
  268. data/lib/capybara/version.rb +2 -1
  269. data/lib/capybara/window.rb +39 -21
  270. data/lib/capybara.rb +208 -186
  271. data/spec/basic_node_spec.rb +52 -39
  272. data/spec/capybara_spec.rb +72 -50
  273. data/spec/css_builder_spec.rb +101 -0
  274. data/spec/css_splitter_spec.rb +38 -0
  275. data/spec/dsl_spec.rb +81 -61
  276. data/spec/filter_set_spec.rb +46 -0
  277. data/spec/fixtures/capybara.csv +1 -0
  278. data/spec/fixtures/certificate.pem +25 -0
  279. data/spec/fixtures/key.pem +27 -0
  280. data/spec/fixtures/selenium_driver_rspec_failure.rb +7 -3
  281. data/spec/fixtures/selenium_driver_rspec_success.rb +7 -3
  282. data/spec/minitest_spec.rb +164 -0
  283. data/spec/minitest_spec_spec.rb +162 -0
  284. data/spec/per_session_config_spec.rb +68 -0
  285. data/spec/rack_test_spec.rb +189 -96
  286. data/spec/regexp_dissassembler_spec.rb +250 -0
  287. data/spec/result_spec.rb +143 -13
  288. data/spec/rspec/features_spec.rb +38 -32
  289. data/spec/rspec/scenarios_spec.rb +9 -7
  290. data/spec/rspec/shared_spec_matchers.rb +959 -0
  291. data/spec/rspec/views_spec.rb +9 -3
  292. data/spec/rspec_matchers_spec.rb +62 -0
  293. data/spec/rspec_spec.rb +127 -30
  294. data/spec/sauce_spec_chrome.rb +43 -0
  295. data/spec/selector_spec.rb +458 -37
  296. data/spec/selenium_spec_chrome.rb +196 -9
  297. data/spec/selenium_spec_chrome_remote.rb +100 -0
  298. data/spec/selenium_spec_edge.rb +47 -0
  299. data/spec/selenium_spec_firefox.rb +210 -0
  300. data/spec/selenium_spec_firefox_remote.rb +80 -0
  301. data/spec/selenium_spec_ie.rb +150 -0
  302. data/spec/selenium_spec_safari.rb +148 -0
  303. data/spec/server_spec.rb +200 -101
  304. data/spec/session_spec.rb +91 -0
  305. data/spec/shared_selenium_node.rb +83 -0
  306. data/spec/shared_selenium_session.rb +558 -0
  307. data/spec/spec_helper.rb +94 -2
  308. data/spec/xpath_builder_spec.rb +93 -0
  309. metadata +420 -60
  310. data/lib/capybara/query.rb +0 -7
  311. data/lib/capybara/spec/session/assert_current_path.rb +0 -60
  312. data/lib/capybara/spec/session/assert_selector.rb +0 -148
  313. data/lib/capybara/spec/session/assert_text.rb +0 -196
  314. data/lib/capybara/spec/session/assert_title.rb +0 -70
  315. data/lib/capybara/spec/session/source_spec.rb +0 -0
  316. data/lib/capybara/spec/session/within_frame_spec.rb +0 -53
  317. data/spec/rspec/matchers_spec.rb +0 -827
  318. data/spec/selenium_spec.rb +0 -151
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Capybara::Session do
6
+ describe '#new' do
7
+ it 'should raise an error if passed non-existent driver' do
8
+ expect do
9
+ described_class.new(:quox, TestApp).driver
10
+ end.to raise_error(Capybara::DriverNotFoundError)
11
+ end
12
+
13
+ it 'verifies a passed app is a rack app' do
14
+ expect do
15
+ described_class.new(:unknown, random: 'hash')
16
+ end.to raise_error TypeError, 'The second parameter to Session::new should be a rack app if passed.'
17
+ end
18
+ end
19
+
20
+ context 'current_driver' do
21
+ around do |example|
22
+ orig_driver = Capybara.current_driver
23
+ example.run
24
+ Capybara.current_driver = orig_driver
25
+ end
26
+
27
+ it 'is global when threadsafe false' do
28
+ Capybara.threadsafe = false
29
+ Capybara.current_driver = :selenium
30
+ thread = Thread.new do
31
+ Capybara.current_driver = :random
32
+ end
33
+ thread.join
34
+ expect(Capybara.current_driver).to eq :random
35
+ end
36
+
37
+ it 'is thread specific threadsafe true' do
38
+ Capybara.threadsafe = true
39
+ Capybara.current_driver = :selenium
40
+ thread = Thread.new do
41
+ Capybara.current_driver = :random
42
+ end
43
+ thread.join
44
+ expect(Capybara.current_driver).to eq :selenium
45
+ end
46
+ end
47
+
48
+ context 'session_name' do
49
+ around do |example|
50
+ orig_name = Capybara.session_name
51
+ example.run
52
+ Capybara.session_name = orig_name
53
+ end
54
+
55
+ it 'is global when threadsafe false' do
56
+ Capybara.threadsafe = false
57
+ Capybara.session_name = 'sess1'
58
+ thread = Thread.new do
59
+ Capybara.session_name = 'sess2'
60
+ end
61
+ thread.join
62
+ expect(Capybara.session_name).to eq 'sess2'
63
+ end
64
+
65
+ it 'is thread specific when threadsafe true' do
66
+ Capybara.threadsafe = true
67
+ Capybara.session_name = 'sess1'
68
+ thread = Thread.new do
69
+ Capybara.session_name = 'sess2'
70
+ end
71
+ thread.join
72
+ expect(Capybara.session_name).to eq 'sess1'
73
+ end
74
+ end
75
+
76
+ context 'quit' do
77
+ it 'will reset the driver' do
78
+ session = described_class.new(:rack_test, TestApp)
79
+ driver = session.driver
80
+ session.quit
81
+ expect(session.driver).not_to eql driver
82
+ end
83
+
84
+ it 'resets the document' do
85
+ session = described_class.new(:rack_test, TestApp)
86
+ document = session.document
87
+ session.quit
88
+ expect(session.document.base).not_to eql document.base
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'selenium-webdriver'
5
+
6
+ RSpec.shared_examples 'Capybara::Node' do |session, _mode|
7
+ let(:session) { session }
8
+
9
+ describe '#content_editable?' do
10
+ it 'returns true when the element is content editable' do
11
+ session.visit('/with_js')
12
+ expect(session.find(:css, '#existing_content_editable').base.content_editable?).to be true
13
+ expect(session.find(:css, '#existing_content_editable_child').base.content_editable?).to be true
14
+ end
15
+
16
+ it 'returns false when the element is not content editable' do
17
+ session.visit('/with_js')
18
+ expect(session.find(:css, '#drag').base.content_editable?).to be false
19
+ end
20
+ end
21
+
22
+ describe '#send_keys' do
23
+ it 'should process space' do
24
+ session.visit('/form')
25
+ session.find(:css, '#address1_city').send_keys('ocean', [:shift, :space, 'side'])
26
+ expect(session.find(:css, '#address1_city').value).to eq 'ocean SIDE'
27
+ end
28
+ end
29
+
30
+ describe '#[]' do
31
+ it 'should work for spellcheck' do
32
+ session.visit('/with_html')
33
+ expect(session.find('//input[@spellcheck="TRUE"]')[:spellcheck]).to eq('true')
34
+ expect(session.find('//input[@spellcheck="FALSE"]')[:spellcheck]).to eq('false')
35
+ end
36
+ end
37
+
38
+ describe '#set' do
39
+ it 'respects maxlength when using rapid set' do
40
+ session.visit('/form')
41
+ inp = session.find(:css, '#long_length')
42
+ value = (0...50).map { |i| ((i % 26) + 65).chr }.join
43
+ inp.set(value, rapid: true)
44
+ expect(inp.value).to eq value[0...35]
45
+ end
46
+ end
47
+
48
+ describe '#visible?' do
49
+ let(:bridge) do
50
+ session.driver.browser.send(:bridge)
51
+ end
52
+
53
+ around do |example|
54
+ native_displayed = session.driver.options[:native_displayed]
55
+ example.run
56
+ session.driver.options[:native_displayed] = native_displayed
57
+ end
58
+
59
+ before do
60
+ allow(bridge).to receive(:execute_atom).and_call_original
61
+ end
62
+
63
+ it 'will use native displayed if told to' do
64
+ pending "Chromedriver < 76.0.3809.25 doesn't support native displayed in W3C mode" if chrome_lt?(76, session) && (ENV['W3C'] != 'false')
65
+
66
+ session.driver.options[:native_displayed] = true
67
+ session.visit('/form')
68
+ session.find(:css, '#address1_city', visible: true)
69
+
70
+ expect(bridge).not_to have_received(:execute_atom)
71
+ end
72
+
73
+ it "won't use native displayed if told not to" do
74
+ skip 'Non-W3C uses native' if chrome?(session) && (ENV['W3C'] == 'false')
75
+
76
+ session.driver.options[:native_displayed] = false
77
+ session.visit('/form')
78
+ session.find(:css, '#address1_city', visible: true)
79
+
80
+ expect(bridge).to have_received(:execute_atom).with(:isDisplayed, any_args)
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,558 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'selenium-webdriver'
5
+
6
+ RSpec.shared_examples 'Capybara::Session' do |session, mode|
7
+ let(:session) { session }
8
+
9
+ context 'with selenium driver' do
10
+ describe '#driver' do
11
+ it 'should be a selenium driver' do
12
+ expect(session.driver).to be_an_instance_of(Capybara::Selenium::Driver)
13
+ end
14
+ end
15
+
16
+ describe '#mode' do
17
+ it 'should remember the mode' do
18
+ expect(session.mode).to eq(mode)
19
+ end
20
+ end
21
+
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 eq false
27
+ end
28
+ end
29
+
30
+ describe 'exit codes' do
31
+ let(:env) { { 'SELENIUM_BROWSER' => session.driver.options[:browser].to_s } }
32
+ let!(:orig_dir) { Dir.getwd }
33
+
34
+ before do
35
+ Dir.chdir(File.join(File.dirname(__FILE__), '..'))
36
+ end
37
+
38
+ after do
39
+ Dir.chdir(orig_dir)
40
+ end
41
+
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)
48
+ end
49
+
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)
56
+ end
57
+ end
58
+
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))
65
+ end
66
+
67
+ it 'can be called before visiting' do
68
+ session.accept_alert 'Initial alert' do
69
+ session.visit('/initial_alert')
70
+ end
71
+ expect(session).to have_text('Initial alert page')
72
+ end
73
+ end
74
+
75
+ describe '#fill_in_with empty string and no options' do
76
+ it 'should trigger change when clearing a field' do
77
+ pending "safaridriver doesn't trigger change for clear" if safari?(session)
78
+ session.visit('/with_js')
79
+ session.fill_in('with_change_event', with: '')
80
+ # click outside the field to trigger the change event
81
+ session.find(:css, 'body').click
82
+ expect(session).to have_selector(:css, '.change_event_triggered', match: :one)
83
+ end
84
+ end
85
+
86
+ describe '#fill_in with { :clear => :backspace } fill_option', requires: [:js] do
87
+ before do
88
+ # Firefox has an issue with change events if the main window doesn't think it's focused
89
+ session.execute_script('window.focus()')
90
+ end
91
+
92
+ it 'should fill in a field, replacing an existing value' do
93
+ session.visit('/form')
94
+ session.fill_in('form_first_name',
95
+ with: 'Harry',
96
+ fill_options: { clear: :backspace })
97
+ expect(session.find(:fillable_field, 'form_first_name').value).to eq('Harry')
98
+ end
99
+
100
+ it 'should fill in a field, replacing an existing value, even with caret position' do
101
+ session.visit('/form')
102
+ session.find(:css, '#form_first_name').execute_script <<-JS
103
+ this.focus();
104
+ this.setSelectionRange(0, 0);
105
+ JS
106
+
107
+ session.fill_in('form_first_name',
108
+ with: 'Harry',
109
+ fill_options: { clear: :backspace })
110
+ expect(session.find(:fillable_field, 'form_first_name').value).to eq('Harry')
111
+ end
112
+
113
+ it 'should fill in if the option is set via global option' do
114
+ Capybara.default_set_options = { clear: :backspace }
115
+ session.visit('/form')
116
+ session.fill_in('form_first_name', with: 'Thomas')
117
+ expect(session.find(:fillable_field, 'form_first_name').value).to eq('Thomas')
118
+ end
119
+
120
+ it 'should only trigger onchange once' do
121
+ session.visit('/with_js')
122
+ sleep 2 if safari?(session) # Safari needs a delay (to load event handlers maybe ???)
123
+ session.fill_in('with_change_event',
124
+ with: 'some value',
125
+ fill_options: { clear: :backspace })
126
+ # click outside the field to trigger the change event
127
+ session.find(:css, '#with_focus_event').click
128
+ expect(session.find(:css, '.change_event_triggered', match: :one, wait: 5)).to have_text 'some value'
129
+ end
130
+
131
+ it 'should trigger change when clearing field' do
132
+ session.visit('/with_js')
133
+ session.fill_in('with_change_event',
134
+ with: '',
135
+ fill_options: { clear: :backspace })
136
+ # click outside the field to trigger the change event
137
+ session.find(:css, '#with_focus_event').click
138
+ expect(session).to have_selector(:css, '.change_event_triggered', match: :one, wait: 5)
139
+ end
140
+
141
+ it 'should trigger input event field_value.length times' do
142
+ session.visit('/with_js')
143
+ session.fill_in('with_change_event',
144
+ with: '',
145
+ fill_options: { clear: :backspace })
146
+ # click outside the field to trigger the change event
147
+ # session.find(:css, 'body').click
148
+ session.find(:css, 'h1', text: 'FooBar').click
149
+ expect(session).to have_xpath('//p[@class="input_event_triggered"]', count: 13)
150
+ end
151
+ end
152
+
153
+ describe '#fill_in with { clear: :none } fill_options' do
154
+ it 'should append to content in a field' do
155
+ pending 'Safari overwrites by default - need to figure out a workaround' if safari?(session)
156
+
157
+ session.visit('/form')
158
+ session.fill_in('form_first_name',
159
+ with: 'Harry',
160
+ fill_options: { clear: :none })
161
+ expect(session.find(:fillable_field, 'form_first_name').value).to eq('JohnHarry')
162
+ end
163
+
164
+ it 'works with rapid fill' do
165
+ pending 'Safari overwrites by default - need to figure out a workaround' if safari?(session)
166
+
167
+ long_string = (0...60).map { |i| ((i % 26) + 65).chr }.join
168
+ session.visit('/form')
169
+ session.fill_in('form_first_name', with: long_string, fill_options: { clear: :none })
170
+ expect(session.find(:fillable_field, 'form_first_name').value).to eq("John#{long_string}")
171
+ end
172
+ end
173
+
174
+ describe '#fill_in with Date' do
175
+ before do
176
+ session.visit('/form')
177
+ session.find(:css, '#form_date').execute_script <<-JS
178
+ window.capybara_formDateFiredEvents = [];
179
+ var fd = this;
180
+ ['focus', 'input', 'change'].forEach(function(eventType) {
181
+ fd.addEventListener(eventType, function() { window.capybara_formDateFiredEvents.push(eventType); });
182
+ });
183
+ JS
184
+ # work around weird FF issue where it would create an extra focus issue in some cases
185
+ session.find(:css, 'h1', text: 'Form').click
186
+ # session.find(:css, 'body').click
187
+ end
188
+
189
+ it 'should generate standard events on changing value' do
190
+ pending "IE 11 doesn't support date input type" if ie?(session)
191
+ pending "Safari doesn't support date input type" if safari?(session)
192
+ session.fill_in('form_date', with: Date.today)
193
+ expect(session.evaluate_script('window.capybara_formDateFiredEvents')).to eq %w[focus input change]
194
+ end
195
+
196
+ it 'should not generate input and change events if the value is not changed' do
197
+ pending "IE 11 doesn't support date input type" if ie?(session)
198
+ pending "Safari doesn't support date input type" if safari?(session)
199
+ session.fill_in('form_date', with: Date.today)
200
+ session.fill_in('form_date', with: Date.today)
201
+ # Chrome adds an extra focus for some reason - ok for now
202
+ expect(session.evaluate_script('window.capybara_formDateFiredEvents')).to eq(%w[focus input change])
203
+ end
204
+ end
205
+
206
+ describe '#fill_in with { clear: Array } fill_options' do
207
+ it 'should pass the array through to the element' do
208
+ # this is mainly for use with [[:control, 'a'], :backspace] - however since that is platform dependant I'm testing with something less useful
209
+ session.visit('/form')
210
+ session.fill_in('form_first_name',
211
+ with: 'Harry',
212
+ fill_options: { clear: [[:shift, 'abc'], :backspace] })
213
+ expect(session.find(:fillable_field, 'form_first_name').value).to eq('JohnABHarry')
214
+ end
215
+ end
216
+
217
+ describe '#fill_in with Emoji' do
218
+ it 'sends emojis' do
219
+ session.visit('/form')
220
+ session.fill_in('form_first_name', with: 'a😀cd😴 🛌🏽🇵🇹 e🤾🏽‍♀️f')
221
+ expect(session.find(:fillable_field, 'form_first_name').value).to eq('a😀cd😴 🛌🏽🇵🇹 e🤾🏽‍♀️f')
222
+ end
223
+ end
224
+
225
+ describe '#send_keys' do
226
+ it 'defaults to sending keys to the document.activeElement' do
227
+ session.visit('/form')
228
+
229
+ expect(session.evaluate_script('document.activeElement')).to eq(session.find('//body'))
230
+
231
+ session.send_keys(:tab)
232
+
233
+ expect(session.evaluate_script('document.activeElement')).to eq(session.first(:field))
234
+ end
235
+ end
236
+
237
+ describe '#path' do
238
+ it 'returns xpath' do
239
+ # this is here because it is testing for an XPath that is specific to the algorithm used in the selenium driver
240
+ session.visit('/path')
241
+ element = session.find(:link, 'Second Link')
242
+ expect(element.path).to eq('/HTML/BODY[1]/DIV[2]/A[1]')
243
+ end
244
+
245
+ it 'handles namespaces in xhtml' do
246
+ pending "IE 11 doesn't handle all XPath querys (namespace-uri, etc)" if ie?(session)
247
+ session.visit '/with_namespace'
248
+ rect = session.find(:css, 'div svg rect:first-of-type')
249
+ 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]")
250
+ expect(session.find(:xpath, rect.path)).to eq rect
251
+ end
252
+
253
+ it 'handles default namespaces in html5' do
254
+ pending "IE 11 doesn't handle all XPath querys (namespace-uri, etc)" if ie?(session)
255
+ session.visit '/with_html5_svg'
256
+ rect = session.find(:css, 'div svg rect:first-of-type')
257
+ 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]")
258
+ expect(session.find(:xpath, rect.path)).to eq rect
259
+ end
260
+
261
+ it 'handles case sensitive element names' do
262
+ pending "IE 11 doesn't handle all XPath querys (namespace-uri, etc)" if ie?(session)
263
+ session.visit '/with_namespace'
264
+ els = session.all(:css, 'div *', visible: :all)
265
+ expect { els.map(&:path) }.not_to raise_error
266
+ lg = session.find(:css, 'div linearGradient', visible: :all)
267
+ expect(session.find(:xpath, lg.path, visible: :all)).to eq lg
268
+ end
269
+ end
270
+
271
+ describe 'all with disappearing elements' do
272
+ it 'ignores stale elements in results' do
273
+ session.visit('/path')
274
+ elements = session.all(:link) { |_node| raise Selenium::WebDriver::Error::StaleElementReferenceError }
275
+ expect(elements.size).to eq 0
276
+ end
277
+ end
278
+
279
+ describe '#evaluate_script' do
280
+ it 'can return an element' do
281
+ session.visit('/form')
282
+ element = session.evaluate_script("document.getElementById('form_title')")
283
+ expect(element).to eq session.find(:id, 'form_title')
284
+ end
285
+
286
+ it 'can return arrays of nested elements' do
287
+ session.visit('/form')
288
+ elements = session.evaluate_script('document.querySelectorAll("#form_city option")')
289
+ expect(elements).to all(be_instance_of Capybara::Node::Element)
290
+ expect(elements).to eq session.find(:css, '#form_city').all(:css, 'option').to_a
291
+ end
292
+
293
+ it 'can return hashes with elements' do
294
+ session.visit('/form')
295
+ result = session.evaluate_script("{ a: document.getElementById('form_title'), b: {c: document.querySelectorAll('#form_city option')}}")
296
+ expect(result).to eq(
297
+ 'a' => session.find(:id, 'form_title'),
298
+ 'b' => {
299
+ 'c' => session.find(:css, '#form_city').all(:css, 'option').to_a
300
+ }
301
+ )
302
+ end
303
+
304
+ describe '#evaluate_async_script' do
305
+ it 'will timeout if the script takes too long' do
306
+ skip 'safaridriver returns the wrong error type' if safari?(session)
307
+ session.visit('/with_js')
308
+ expect do
309
+ session.using_wait_time(1) do
310
+ session.evaluate_async_script('var cb = arguments[0]; setTimeout(function(){ cb(null) }, 3000)')
311
+ end
312
+ end.to raise_error Selenium::WebDriver::Error::ScriptTimeoutError
313
+ end
314
+ end
315
+ end
316
+
317
+ describe 'Element#inspect' do
318
+ it 'outputs obsolete elements' do
319
+ session.visit('/form')
320
+ el = session.find(:button, 'Click me!').click
321
+ expect(session).to have_no_button('Click me!')
322
+ allow(el).to receive(:synchronize)
323
+ expect(el.inspect).to eq 'Obsolete #<Capybara::Node::Element>'
324
+ expect(el).not_to have_received(:synchronize)
325
+ end
326
+ end
327
+
328
+ describe 'Element#click' do
329
+ it 'should handle fixed headers/footers' do
330
+ session.visit('/with_fixed_header_footer')
331
+ session.using_wait_time(2) do
332
+ session.find(:link, 'Go to root').click
333
+ end
334
+ expect(session).to have_current_path('/')
335
+ end
336
+ end
337
+
338
+ describe 'Capybara#Node#attach_file' do
339
+ it 'can attach a directory' do
340
+ pending "Geckodriver doesn't support uploading a directory" if firefox?(session)
341
+ pending "Selenium remote doesn't support transferring a directory" if remote?(session)
342
+ 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) && ENV['HEADLESS']
343
+ pending "IE doesn't support uploading a directory" if ie?(session)
344
+ pending 'Chrome/chromedriver 73 breaks this' if chrome?(session) && chrome_gte?(73, session) && chrome_lt?(75, session)
345
+ pending "Safari doesn't support uploading a directory" if safari?(session)
346
+ # pending "Edge/msedgedriver doesn't support directory upload" if edge?(session) && edge_gte?(75, session)
347
+
348
+ session.visit('/form')
349
+ test_file_dir = File.expand_path('./fixtures', File.dirname(__FILE__))
350
+ session.attach_file('Directory Upload', test_file_dir)
351
+ session.click_button('Upload Multiple')
352
+ expect(session.body).to include('5 | ') # number of files
353
+ end
354
+
355
+ it 'can attach a relative file' do
356
+ pending 'Geckdoriver on windows requires alternate file separator which path expansion replaces' if Gem.win_platform? && firefox?(session)
357
+
358
+ session.visit('/form')
359
+ session.attach_file('Single Document', 'spec/fixtures/capybara.csv')
360
+ session.click_button('Upload Single')
361
+ expect(session.body).to include('Content-type: text/csv')
362
+ end
363
+ end
364
+
365
+ context 'Windows' do
366
+ it "can't close the primary window" do
367
+ expect do
368
+ session.current_window.close
369
+ end.to raise_error(ArgumentError, 'Not allowed to close the primary window')
370
+ end
371
+ end
372
+
373
+ # rubocop:disable RSpec/InstanceVariable
374
+ describe 'Capybara#disable_animation' do
375
+ context 'when set to `true`' do
376
+ before(:context) do # rubocop:disable RSpec/BeforeAfterAll
377
+ skip "Safari doesn't support multiple sessions" if safari?(session)
378
+ # NOTE: Although Capybara.SpecHelper.reset! sets Capybara.disable_animation to false,
379
+ # it doesn't affect any of these tests because the settings are applied per-session
380
+ Capybara.disable_animation = true
381
+ @animation_session = Capybara::Session.new(session.mode, TestApp.new)
382
+ end
383
+
384
+ it 'should disable CSS transitions' do
385
+ @animation_session.visit('with_animation')
386
+ @animation_session.click_link('transition me away')
387
+ expect(@animation_session).to have_no_link('transition me away', wait: 0.5)
388
+ end
389
+
390
+ it 'should disable CSS animations (set to 0s)' do
391
+ @animation_session.visit('with_animation')
392
+ sleep 1
393
+ @animation_session.click_link('animate me away')
394
+ expect(@animation_session).to have_no_link('animate me away', wait: 0.5)
395
+ end
396
+
397
+ it 'should disable CSS animations on pseudo elements (set to 0s)' do
398
+ @animation_session.visit('with_animation')
399
+ sleep 1
400
+ @animation_session.find_link('animate me away').right_click
401
+ expect(@animation_session).to have_content('Animation Ended', wait: 0.1)
402
+ end
403
+
404
+ it 'should scroll the page instantly', requires: [:js] do
405
+ @animation_session.visit('with_animation')
406
+ scroll_y = @animation_session.evaluate_script(<<~JS)
407
+ (function(){
408
+ window.scrollTo(0,500);
409
+ return window.scrollY;
410
+ })()
411
+ JS
412
+ expect(scroll_y).to eq 500
413
+ end
414
+
415
+ it 'should scroll the page instantly without jquery animation', requires: [:js] do
416
+ @animation_session.visit('with_jquery_animation')
417
+ @animation_session.click_link('scroll top 500')
418
+ scroll_y = @animation_session.evaluate_script('window.scrollY')
419
+ expect(scroll_y).to eq 500
420
+ end
421
+ end
422
+
423
+ context 'when set to `false`' do
424
+ before(:context) do # rubocop:disable RSpec/BeforeAfterAll
425
+ skip "Safari doesn't support multiple sessions" if safari?(session)
426
+ # NOTE: Although Capybara.SpecHelper.reset! sets Capybara.disable_animation to false,
427
+ # it doesn't affect any of these tests because the settings are applied per-session
428
+ Capybara.disable_animation = false
429
+ @animation_session = Capybara::Session.new(session.mode, TestApp.new)
430
+ end
431
+
432
+ it 'should scroll the page with a smooth animation', requires: [:js] do
433
+ @animation_session.visit('with_animation')
434
+ scroll_y = @animation_session.evaluate_script(<<~JS)
435
+ (function(){
436
+ window.scrollTo(0,500);
437
+ return window.scrollY;
438
+ })()
439
+ JS
440
+ # measured over 0.5 seconds: 0, 75, 282, 478, 500
441
+ expect(scroll_y).to be < 500
442
+ end
443
+
444
+ it 'should scroll the page with jquery animation', requires: [:js] do
445
+ @animation_session.visit('with_jquery_animation')
446
+ @animation_session.click_link('scroll top 500')
447
+ scroll_y = @animation_session.evaluate_script('window.scrollY')
448
+ expect(scroll_y).to be < 500
449
+ end
450
+ end
451
+
452
+ context 'if we pass in css that matches elements' do
453
+ before(:context) do # rubocop:disable RSpec/BeforeAfterAll
454
+ skip "safaridriver doesn't support multiple sessions" if safari?(session)
455
+ # NOTE: Although Capybara.SpecHelper.reset! sets Capybara.disable_animation to false,
456
+ # it doesn't affect any of these tests because the settings are applied per-session
457
+ Capybara.disable_animation = '#with_animation a'
458
+ @animation_session_with_matching_css = Capybara::Session.new(session.mode, TestApp.new)
459
+ end
460
+
461
+ it 'should disable CSS transitions' do
462
+ @animation_session_with_matching_css.visit('with_animation')
463
+ sleep 1
464
+ @animation_session_with_matching_css.click_link('transition me away')
465
+ expect(@animation_session_with_matching_css).to have_no_link('transition me away', wait: 0.5)
466
+ end
467
+
468
+ it 'should disable CSS animations' do
469
+ @animation_session_with_matching_css.visit('with_animation')
470
+ sleep 1
471
+ @animation_session_with_matching_css.click_link('animate me away')
472
+ expect(@animation_session_with_matching_css).to have_no_link('animate me away', wait: 0.5)
473
+ end
474
+ end
475
+
476
+ context 'if we pass in css that does not match elements' do
477
+ before(:context) do # rubocop:disable RSpec/BeforeAfterAll
478
+ skip "Safari doesn't support multiple sessions" if safari?(session)
479
+ # NOTE: Although Capybara.SpecHelper.reset! sets Capybara.disable_animation to false,
480
+ # it doesn't affect any of these tests because the settings are applied per-session
481
+ Capybara.disable_animation = '.this-class-matches-nothing'
482
+ @animation_session_without_matching_css = Capybara::Session.new(session.mode, TestApp.new)
483
+ end
484
+
485
+ it 'should not disable CSS transitions' do
486
+ @animation_session_without_matching_css.visit('with_animation')
487
+ sleep 1
488
+ @animation_session_without_matching_css.click_link('transition me away')
489
+ sleep 0.5 # Wait long enough for click to have been processed
490
+ expect(@animation_session_without_matching_css).to have_link('transition me away', wait: false)
491
+ expect(@animation_session_without_matching_css).to have_no_link('transition me away', wait: 5)
492
+ end
493
+
494
+ it 'should not disable CSS animations' do
495
+ @animation_session_without_matching_css.visit('with_animation')
496
+ sleep 1
497
+ @animation_session_without_matching_css.click_link('animate me away')
498
+ sleep 0.5 # Wait long enough for click to have been processed
499
+ expect(@animation_session_without_matching_css).to have_link('animate me away', wait: false)
500
+ expect(@animation_session_without_matching_css).to have_no_link('animate me away', wait: 5)
501
+ end
502
+ end
503
+ end
504
+ # rubocop:enable RSpec/InstanceVariable
505
+
506
+ describe ':element selector' do
507
+ it 'can find html5 svg elements' do
508
+ session.visit('with_html5_svg')
509
+ expect(session).to have_selector(:element, :svg)
510
+ expect(session).to have_selector(:element, :rect, visible: :visible)
511
+ expect(session).to have_selector(:element, :circle)
512
+ expect(session).to have_selector(:element, :linearGradient, visible: :all)
513
+ end
514
+
515
+ it 'can query attributes with strange characters' do
516
+ session.visit('/form')
517
+ expect(session).to have_selector(:element, "{custom}": true)
518
+ expect(session).to have_selector(:element, "{custom}": 'abcdef')
519
+ end
520
+ end
521
+
522
+ describe 'with react' do
523
+ context 'controlled components' do
524
+ it 'can set and clear a text field' do
525
+ skip "This test doesn't support older browsers" if ie?(session)
526
+
527
+ session.visit 'react'
528
+
529
+ session.fill_in('Name:', with: 'abc')
530
+ session.accept_prompt 'A name was submitted: abc' do
531
+ session.click_button('Submit')
532
+ end
533
+
534
+ session.fill_in('Name:', with: '')
535
+ session.accept_prompt(/A name was submitted: $/) do
536
+ session.click_button('Submit')
537
+ end
538
+ end
539
+
540
+ it 'works with rapid fill' do
541
+ skip "This test doesn't support older browsers" if ie?(session)
542
+
543
+ session.visit 'react'
544
+ long_string = (0...60).map { |i| ((i % 26) + 65).chr }.join
545
+
546
+ session.fill_in('Name:', with: long_string)
547
+ session.accept_prompt "A name was submitted: #{long_string}" do
548
+ session.click_button('Submit')
549
+ end
550
+ end
551
+ end
552
+ end
553
+ end
554
+
555
+ def headless_or_remote?
556
+ !ENV['HEADLESS'].nil? || session.driver.options[:browser] == :remote
557
+ end
558
+ end