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
@@ -1,64 +1,84 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  Capybara::SpecHelper.spec '#has_xpath?' do
3
4
  before do
4
5
  @session.visit('/with_html')
5
6
  end
6
7
 
7
- it "should be true if the given selector is on the page" do
8
- expect(@session).to have_xpath("//p")
8
+ it 'should be true if the given selector is on the page' do
9
+ expect(@session).to have_xpath('//p')
9
10
  expect(@session).to have_xpath("//p//a[@id='foo']")
10
11
  expect(@session).to have_xpath("//p[contains(.,'est')]")
11
12
  end
12
13
 
13
- it "should be false if the given selector is not on the page" do
14
- expect(@session).not_to have_xpath("//abbr")
14
+ it 'should support :id option' do
15
+ expect(@session).to have_xpath('//h2', id: 'h2one')
16
+ expect(@session).to have_xpath('//h2')
17
+ expect(@session).to have_xpath('//h2', id: /h2o/)
18
+ end
19
+
20
+ it 'should support :class option' do
21
+ expect(@session).to have_xpath('//li', class: 'guitarist')
22
+ expect(@session).to have_xpath('//li', class: /guitar/)
23
+ expect(@session).to have_xpath('//li', class: /guitar|drummer/)
24
+ expect(@session).to have_xpath('//li', class: %w[beatle guitarist])
25
+ expect(@session).to have_xpath('//li', class: /.*/)
26
+ end
27
+
28
+ it 'should be false if the given selector is not on the page' do
29
+ expect(@session).not_to have_xpath('//abbr')
15
30
  expect(@session).not_to have_xpath("//p//a[@id='doesnotexist']")
16
31
  expect(@session).not_to have_xpath("//p[contains(.,'thisstringisnotonpage')]")
17
32
  end
18
33
 
19
- it "should use xpath even if default selector is CSS" do
34
+ it 'should use xpath even if default selector is CSS' do
20
35
  Capybara.default_selector = :css
21
36
  expect(@session).not_to have_xpath("//p//a[@id='doesnotexist']")
22
37
  end
23
38
 
24
- it "should respect scopes" do
39
+ it 'should respect scopes' do
25
40
  @session.within "//p[@id='first']" do
26
41
  expect(@session).to have_xpath(".//a[@id='foo']")
27
42
  expect(@session).not_to have_xpath(".//a[@id='red']")
28
43
  end
29
44
  end
30
45
 
31
- it "should wait for content to appear", :requires => [:js] do
32
- @session.visit('/with_js')
33
- @session.click_link('Click me')
34
- expect(@session).to have_xpath("//input[@type='submit' and @value='New Here']")
46
+ it 'should wait for content to appear', requires: [:js] do
47
+ Capybara.using_wait_time(3) do
48
+ @session.visit('/with_js')
49
+ @session.click_link('Click me') # updates page after 500ms
50
+ expect(@session).to have_xpath("//input[@type='submit' and @value='New Here']")
51
+ end
35
52
  end
36
53
 
37
- context "with count" do
38
- it "should be true if the content occurs the given number of times" do
39
- expect(@session).to have_xpath("//p", :count => 3)
40
- expect(@session).to have_xpath("//p//a[@id='foo']", :count => 1)
41
- expect(@session).to have_xpath("//p[contains(.,'est')]", :count => 1)
42
- expect(@session).to have_xpath("//p//a[@id='doesnotexist']", :count => 0)
54
+ context 'with count' do
55
+ it 'should be true if the content occurs the given number of times' do
56
+ expect(@session).to have_xpath('//p', count: 3)
57
+ expect(@session).to have_xpath("//p//a[@id='foo']", count: 1)
58
+ expect(@session).to have_xpath("//p[contains(.,'est')]", count: 1)
59
+ expect(@session).to have_xpath("//p//a[@id='doesnotexist']", count: 0)
60
+ expect(@session).to have_xpath('//li', class: /guitar|drummer/, count: 4)
61
+ expect(@session).to have_xpath('//li', id: /john|paul/, class: /guitar|drummer/, count: 2)
62
+ expect(@session).to have_xpath('//li', class: %w[beatle guitarist], count: 2)
43
63
  end
44
64
 
45
- it "should be false if the content occurs a different number of times than the given" do
46
- expect(@session).not_to have_xpath("//p", :count => 6)
47
- expect(@session).not_to have_xpath("//p//a[@id='foo']", :count => 2)
48
- expect(@session).not_to have_xpath("//p[contains(.,'est')]", :count => 5)
49
- expect(@session).not_to have_xpath("//p//a[@id='doesnotexist']", :count => 1)
65
+ it 'should be false if the content occurs a different number of times than the given' do
66
+ expect(@session).not_to have_xpath('//p', count: 6)
67
+ expect(@session).not_to have_xpath("//p//a[@id='foo']", count: 2)
68
+ expect(@session).not_to have_xpath("//p[contains(.,'est')]", count: 5)
69
+ expect(@session).not_to have_xpath("//p//a[@id='doesnotexist']", count: 1)
50
70
  end
51
71
  end
52
72
 
53
- context "with text" do
54
- it "should discard all matches where the given string is not contained" do
55
- expect(@session).to have_xpath("//p//a", :text => "Redirect", :count => 1)
56
- expect(@session).not_to have_xpath("//p", :text => "Doesnotexist")
73
+ context 'with text' do
74
+ it 'should discard all matches where the given string is not contained' do
75
+ expect(@session).to have_xpath('//p//a', text: 'Redirect', count: 1)
76
+ expect(@session).not_to have_xpath('//p', text: 'Doesnotexist')
57
77
  end
58
78
 
59
- it "should discard all matches where the given regexp is not matched" do
60
- expect(@session).to have_xpath("//p//a", :text => /re[dab]i/i, :count => 1)
61
- expect(@session).not_to have_xpath("//p//a", :text => /Red$/)
79
+ it 'should discard all matches where the given regexp is not matched' do
80
+ expect(@session).to have_xpath('//p//a', text: /re[dab]i/i, count: 1)
81
+ expect(@session).not_to have_xpath('//p//a', text: /Red$/)
62
82
  end
63
83
  end
64
84
  end
@@ -68,61 +88,62 @@ Capybara::SpecHelper.spec '#has_no_xpath?' do
68
88
  @session.visit('/with_html')
69
89
  end
70
90
 
71
- it "should be false if the given selector is on the page" do
72
- expect(@session).not_to have_no_xpath("//p")
91
+ it 'should be false if the given selector is on the page' do
92
+ expect(@session).not_to have_no_xpath('//p')
73
93
  expect(@session).not_to have_no_xpath("//p//a[@id='foo']")
74
94
  expect(@session).not_to have_no_xpath("//p[contains(.,'est')]")
75
95
  end
76
96
 
77
- it "should be true if the given selector is not on the page" do
78
- expect(@session).to have_no_xpath("//abbr")
97
+ it 'should be true if the given selector is not on the page' do
98
+ expect(@session).to have_no_xpath('//abbr')
79
99
  expect(@session).to have_no_xpath("//p//a[@id='doesnotexist']")
80
100
  expect(@session).to have_no_xpath("//p[contains(.,'thisstringisnotonpage')]")
81
101
  end
82
102
 
83
- it "should use xpath even if default selector is CSS" do
103
+ it 'should use xpath even if default selector is CSS' do
84
104
  Capybara.default_selector = :css
85
105
  expect(@session).to have_no_xpath("//p//a[@id='doesnotexist']")
86
106
  end
87
107
 
88
- it "should respect scopes" do
108
+ it 'should respect scopes' do
89
109
  @session.within "//p[@id='first']" do
90
110
  expect(@session).not_to have_no_xpath(".//a[@id='foo']")
91
111
  expect(@session).to have_no_xpath(".//a[@id='red']")
92
112
  end
93
113
  end
94
114
 
95
- it "should wait for content to disappear", :requires => [:js] do
115
+ it 'should wait for content to disappear', requires: [:js] do
116
+ Capybara.default_max_wait_time = 2
96
117
  @session.visit('/with_js')
97
118
  @session.click_link('Click me')
98
119
  expect(@session).to have_no_xpath("//p[@id='change']")
99
120
  end
100
121
 
101
- context "with count" do
102
- it "should be false if the content occurs the given number of times" do
103
- expect(@session).not_to have_no_xpath("//p", :count => 3)
104
- expect(@session).not_to have_no_xpath("//p//a[@id='foo']", :count => 1)
105
- expect(@session).not_to have_no_xpath("//p[contains(.,'est')]", :count => 1)
106
- expect(@session).not_to have_no_xpath("//p//a[@id='doesnotexist']", :count => 0)
122
+ context 'with count' do
123
+ it 'should be false if the content occurs the given number of times' do
124
+ expect(@session).not_to have_no_xpath('//p', count: 3)
125
+ expect(@session).not_to have_no_xpath("//p//a[@id='foo']", count: 1)
126
+ expect(@session).not_to have_no_xpath("//p[contains(.,'est')]", count: 1)
127
+ expect(@session).not_to have_no_xpath("//p//a[@id='doesnotexist']", count: 0)
107
128
  end
108
129
 
109
- it "should be true if the content occurs a different number of times than the given" do
110
- expect(@session).to have_no_xpath("//p", :count => 6)
111
- expect(@session).to have_no_xpath("//p//a[@id='foo']", :count => 2)
112
- expect(@session).to have_no_xpath("//p[contains(.,'est')]", :count => 5)
113
- expect(@session).to have_no_xpath("//p//a[@id='doesnotexist']", :count => 1)
130
+ it 'should be true if the content occurs a different number of times than the given' do
131
+ expect(@session).to have_no_xpath('//p', count: 6)
132
+ expect(@session).to have_no_xpath("//p//a[@id='foo']", count: 2)
133
+ expect(@session).to have_no_xpath("//p[contains(.,'est')]", count: 5)
134
+ expect(@session).to have_no_xpath("//p//a[@id='doesnotexist']", count: 1)
114
135
  end
115
136
  end
116
137
 
117
- context "with text" do
118
- it "should discard all matches where the given string is contained" do
119
- expect(@session).not_to have_no_xpath("//p//a", :text => "Redirect", :count => 1)
120
- expect(@session).to have_no_xpath("//p", :text => "Doesnotexist")
138
+ context 'with text' do
139
+ it 'should discard all matches where the given string is contained' do
140
+ expect(@session).not_to have_no_xpath('//p//a', text: 'Redirect', count: 1)
141
+ expect(@session).to have_no_xpath('//p', text: 'Doesnotexist')
121
142
  end
122
143
 
123
- it "should discard all matches where the given regexp is matched" do
124
- expect(@session).not_to have_no_xpath("//p//a", :text => /re[dab]i/i, :count => 1)
125
- expect(@session).to have_no_xpath("//p//a", :text => /Red$/)
144
+ it 'should discard all matches where the given regexp is matched' do
145
+ expect(@session).not_to have_no_xpath('//p//a', text: /re[dab]i/i, count: 1)
146
+ expect(@session).to have_no_xpath('//p//a', text: /Red$/)
126
147
  end
127
148
  end
128
149
  end
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  Capybara::SpecHelper.spec '#response_headers' do
3
- it "should return response headers", :requires => [:response_headers] do
4
+ it 'should return response headers', requires: [:response_headers] do
4
5
  @session.visit('/with_simple_html')
5
- expect(@session.response_headers['Content-Type']).to match %r(text/html)
6
+ expect(@session.response_headers['Content-Type']).to match %r{text/html}
6
7
  end
7
8
  end
@@ -1,38 +1,46 @@
1
1
  # frozen_string_literal: true
2
+
3
+ # NOTE: This file uses `sleep` to sync up parts of the tests. This is only implemented like this
4
+ # because of the methods being tested. In tests using Capybara this type of behavior should be implemented
5
+ # using Capybara provided assertions with builtin waiting behavior.
6
+
2
7
  Capybara::SpecHelper.spec '#html' do
3
- it "should return the unmodified page body" do
8
+ it 'should return the unmodified page body' do
4
9
  @session.visit('/')
5
10
  expect(@session.html).to include('Hello world!')
6
11
  end
7
12
 
8
- it "should return the current state of the page", :requires => [:js] do
13
+ it 'should return the current state of the page', requires: [:js] do
9
14
  @session.visit('/with_js')
15
+ sleep 1
10
16
  expect(@session.html).to include('I changed it')
11
17
  expect(@session.html).not_to include('This is text')
12
18
  end
13
19
  end
14
20
 
15
21
  Capybara::SpecHelper.spec '#source' do
16
- it "should return the unmodified page source" do
22
+ it 'should return the unmodified page source' do
17
23
  @session.visit('/')
18
24
  expect(@session.source).to include('Hello world!')
19
25
  end
20
26
 
21
- it "should return the current state of the page", :requires => [:js] do
27
+ it 'should return the current state of the page', requires: [:js] do
22
28
  @session.visit('/with_js')
29
+ sleep 1
23
30
  expect(@session.source).to include('I changed it')
24
31
  expect(@session.source).not_to include('This is text')
25
32
  end
26
33
  end
27
34
 
28
35
  Capybara::SpecHelper.spec '#body' do
29
- it "should return the unmodified page source" do
36
+ it 'should return the unmodified page source' do
30
37
  @session.visit('/')
31
38
  expect(@session.body).to include('Hello world!')
32
39
  end
33
40
 
34
- it "should return the current state of the page", :requires => [:js] do
41
+ it 'should return the current state of the page', requires: [:js] do
35
42
  @session.visit('/with_js')
43
+ sleep 1
36
44
  expect(@session.body).to include('I changed it')
37
45
  expect(@session.body).not_to include('This is text')
38
46
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ Capybara::SpecHelper.spec '#matches_style?', requires: [:css] do
4
+ before do
5
+ @session.visit('/with_html')
6
+ end
7
+
8
+ it 'should be true if the element has the given style' do
9
+ expect(@session.find(:css, '#first')).to match_style(display: 'block')
10
+ expect(@session.find(:css, '#first').matches_style?(display: 'block')).to be true
11
+ expect(@session.find(:css, '#second')).to match_style('display' => 'inline')
12
+ expect(@session.find(:css, '#second').matches_style?('display' => 'inline')).to be true
13
+ end
14
+
15
+ it 'should be false if the element does not have the given style' do
16
+ expect(@session.find(:css, '#first').matches_style?('display' => 'inline')).to be false
17
+ expect(@session.find(:css, '#second').matches_style?(display: 'block')).to be false
18
+ end
19
+
20
+ it 'allows Regexp for value matching' do
21
+ expect(@session.find(:css, '#first')).to match_style(display: /^bl/)
22
+ expect(@session.find(:css, '#first').matches_style?('display' => /^bl/)).to be true
23
+ expect(@session.find(:css, '#first').matches_style?(display: /^in/)).to be false
24
+ end
25
+
26
+ it 'deprecated has_style?' do
27
+ expect { have_style(display: /^bl/) }.to \
28
+ output(/have_style is deprecated/).to_stderr
29
+
30
+ el = @session.find(:css, '#first')
31
+ allow(Capybara::Helpers).to receive(:warn).and_return(nil)
32
+ el.has_style?('display' => /^bl/)
33
+ expect(Capybara::Helpers).to have_received(:warn)
34
+ end
35
+ end
@@ -1,73 +1,88 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- Capybara::SpecHelper.spec "node" do
3
+ # NOTE: This file uses `sleep` to sync up parts of the tests. This is only implemented like this
4
+ # because of the methods being tested. In tests using Capybara this type of behavior should be implemented
5
+ # using Capybara provided assertions with builtin waiting behavior.
6
+
7
+ Capybara::SpecHelper.spec 'node' do
4
8
  before do
5
9
  @session.visit('/with_html')
6
10
  end
7
11
 
8
- it "should act like a session object" do
12
+ it 'should act like a session object' do
9
13
  @session.visit('/form')
10
14
  @form = @session.find(:css, '#get-form')
11
15
  expect(@form).to have_field('Middle Name')
12
16
  expect(@form).to have_no_field('Languages')
13
- @form.fill_in('Middle Name', :with => 'Monkey')
17
+ @form.fill_in('Middle Name', with: 'Monkey')
14
18
  @form.click_button('med')
15
19
  expect(extract_results(@session)['middle_name']).to eq('Monkey')
16
20
  end
17
21
 
18
- it "should scope CSS selectors" do
22
+ it 'should scope CSS selectors' do
19
23
  expect(@session.find(:css, '#second')).to have_no_css('h1')
20
24
  end
21
25
 
22
- describe "#parent" do
23
- it "should have a reference to its parent if there is one" do
24
- @node = @session.find(:css, '#first')
25
- expect(@node.parent).to eq(@node.session.document)
26
- expect(@node.find(:css, '#foo').parent).to eq(@node)
26
+ describe '#query_scope' do
27
+ it 'should have a reference to the element the query was evaluated on if there is one' do
28
+ node = @session.find(:css, '#first')
29
+ expect(node.query_scope).to eq(node.session.document)
30
+ expect(node.find(:css, '#foo').query_scope).to eq(node)
27
31
  end
28
32
  end
29
33
 
30
- describe "#text" do
31
- it "should extract node texts" do
34
+ describe '#text' do
35
+ it 'should extract node texts' do
32
36
  expect(@session.all('//a')[0].text).to eq('labore')
33
37
  expect(@session.all('//a')[1].text).to eq('ullamco')
34
38
  end
35
39
 
36
- it "should return document text on /html selector" do
40
+ it 'should return document text on /html selector' do
37
41
  @session.visit('/with_simple_html')
38
42
  expect(@session.all('/html')[0].text).to eq('Bar')
39
43
  end
40
44
  end
41
45
 
42
- describe "#[]" do
43
- it "should extract node attributes" do
46
+ describe '#[]' do
47
+ it 'should extract node attributes' do
44
48
  expect(@session.all('//a')[0][:class]).to eq('simple')
45
49
  expect(@session.all('//a')[1][:id]).to eq('foo')
46
50
  expect(@session.all('//input')[0][:type]).to eq('text')
47
51
  end
48
52
 
49
- it "should extract boolean node attributes" do
53
+ it 'should extract boolean node attributes' do
50
54
  expect(@session.find('//input[@id="checked_field"]')[:checked]).to be_truthy
51
55
  end
52
56
  end
53
57
 
54
- describe "#value" do
55
- it "should allow retrieval of the value" do
58
+ describe '#style', requires: [:css] do
59
+ it 'should return the computed style value' do
60
+ expect(@session.find(:css, '#first').style('display')).to eq('display' => 'block')
61
+ expect(@session.find(:css, '#second').style(:display)).to eq('display' => 'inline')
62
+ end
63
+
64
+ it 'should return multiple style values' do
65
+ expect(@session.find(:css, '#first').style('display', :'line-height')).to eq('display' => 'block', 'line-height' => '25px')
66
+ end
67
+ end
68
+
69
+ describe '#value' do
70
+ it 'should allow retrieval of the value' do
56
71
  expect(@session.find('//textarea[@id="normal"]').value).to eq('banana')
57
72
  end
58
73
 
59
- it "should not swallow extra newlines in textarea" do
74
+ it 'should not swallow extra newlines in textarea' do
60
75
  expect(@session.find('//textarea[@id="additional_newline"]').value).to eq("\nbanana")
61
76
  end
62
77
 
63
- it "should not swallow leading newlines for set content in textarea" do
78
+ it 'should not swallow leading newlines for set content in textarea' do
64
79
  @session.find('//textarea[@id="normal"]').set("\nbanana")
65
80
  expect(@session.find('//textarea[@id="normal"]').value).to eq("\nbanana")
66
81
  end
67
82
 
68
- it "return any HTML content in textarea" do
69
- @session.find('//textarea[1]').set("some <em>html</em> here")
70
- expect(@session.find('//textarea[1]').value).to eq("some <em>html</em> here")
83
+ it 'return any HTML content in textarea' do
84
+ @session.find('//textarea[1]').set('some <em>html</em> here')
85
+ expect(@session.find('//textarea[1]').value).to eq('some <em>html</em> here')
71
86
  end
72
87
 
73
88
  it "defaults to 'on' for checkbox" do
@@ -81,97 +96,146 @@ Capybara::SpecHelper.spec "node" do
81
96
  end
82
97
  end
83
98
 
84
- describe "#set" do
85
- it "should allow assignment of field value" do
99
+ describe '#set' do
100
+ it 'should allow assignment of field value' do
86
101
  expect(@session.first('//input').value).to eq('monkey')
87
102
  @session.first('//input').set('gorilla')
88
103
  expect(@session.first('//input').value).to eq('gorilla')
89
104
  end
90
105
 
91
- it "should fill the field even if the caret was not at the end", :requires => [:js] do
106
+ it 'should fill the field even if the caret was not at the end', requires: [:js] do
92
107
  @session.execute_script("var el = document.getElementById('test_field'); el.focus(); el.setSelectionRange(0, 0);")
93
108
  @session.first('//input').set('')
94
109
  expect(@session.first('//input').value).to eq('')
95
110
  end
96
111
 
97
- it "should not set if the text field is readonly" do
98
- expect(@session.first('//input[@readonly]').value).to eq('should not change')
99
- @session.first('//input[@readonly]').set('changed')
100
- expect(@session.first('//input[@readonly]').value).to eq('should not change')
101
- end
112
+ if ENV['CAPYBARA_THOROUGH']
113
+ it 'should raise if the text field is readonly' do
114
+ expect { @session.first('//input[@readonly]').set('changed') }.to raise_error(Capybara::ReadOnlyElementError)
115
+ end
102
116
 
103
- it "should raise if the text field is readonly" do
104
- expect(@session.first('//input[@readonly]').set('changed')).to raise_error(Capybara::ReadOnlyElementError)
105
- end if Capybara::VERSION.to_f > 3.0
117
+ it 'should raise if the textarea is readonly' do
118
+ expect { @session.first('//textarea[@readonly]').set('changed') }.to raise_error(Capybara::ReadOnlyElementError)
119
+ end
120
+ else
121
+ it 'should not change if the text field is readonly' do
122
+ @session.first('//input[@readonly]').set('changed')
123
+ expect(@session.first('//input[@readonly]').value).to eq 'should not change'
124
+ end
106
125
 
107
- it "should not set if the textarea is readonly" do
108
- expect(@session.first('//textarea[@readonly]').value).to eq('textarea should not change')
109
- @session.first('//textarea[@readonly]').set('changed')
110
- expect(@session.first('//textarea[@readonly]').value).to eq('textarea should not change')
126
+ it 'should not change if the textarea is readonly' do
127
+ @session.first('//textarea[@readonly]').set('changed')
128
+ expect(@session.first('//textarea[@readonly]').value).to eq 'textarea should not change'
129
+ end
111
130
  end
112
131
 
113
- it "should raise if the text field is readonly" do
114
- expect(@session.first('//textarea[@readonly]').set('changed')).to raise_error(Capybara::ReadOnlyElementError)
115
- end if Capybara::VERSION.to_f > 3.0
116
-
117
- it 'should allow me to change the contents of a contenteditable element', :requires => [:js] do
118
- @session.visit('/with_js')
119
- @session.find(:css,'#existing_content_editable').set('WYSIWYG')
120
- expect(@session.find(:css,'#existing_content_editable').text).to eq('WYSIWYG')
132
+ it 'should use global default options' do
133
+ Capybara.default_set_options = { clear: :backspace }
134
+ element = @session.first(:fillable_field, type: 'text')
135
+ allow(element.base).to receive(:set)
136
+ element.set('gorilla')
137
+ expect(element.base).to have_received(:set).with('gorilla', clear: :backspace)
121
138
  end
122
139
 
123
- it 'should allow me to set the contents of a contenteditable element', :requires => [:js] do
124
- @session.visit('/with_js')
125
- @session.find(:css,'#blank_content_editable').set('WYSIWYG')
126
- expect(@session.find(:css,'#blank_content_editable').text).to eq('WYSIWYG')
127
- end
140
+ context 'with a contenteditable element', requires: [:js] do
141
+ it 'should allow me to change the contents' do
142
+ @session.visit('/with_js')
143
+ @session.find(:css, '#existing_content_editable').set('WYSIWYG')
144
+ expect(@session.find(:css, '#existing_content_editable').text).to eq('WYSIWYG')
145
+ end
128
146
 
129
- it 'should allow me to change the contents of a contenteditable elements child', :requires => [:js] do
130
- pending "Selenium doesn't like editing nested contents"
131
- @session.visit('/with_js')
132
- @session.find(:css,'#existing_content_editable_child').set('WYSIWYG')
133
- expect(@session.find(:css,'#existing_content_editable_child').text).to eq('WYSIWYG')
147
+ it 'should allow me to set the contents' do
148
+ @session.visit('/with_js')
149
+ @session.find(:css, '#blank_content_editable').set('WYSIWYG')
150
+ expect(@session.find(:css, '#blank_content_editable').text).to eq('WYSIWYG')
151
+ end
152
+
153
+ it 'should allow me to change the contents of a child element' do
154
+ @session.visit('/with_js')
155
+ @session.find(:css, '#existing_content_editable_child').set('WYSIWYG')
156
+ expect(@session.find(:css, '#existing_content_editable_child').text).to eq('WYSIWYG')
157
+ expect(@session.find(:css, '#existing_content_editable_child_parent').text).to eq("Some content\nWYSIWYG")
158
+ end
134
159
  end
135
160
  end
136
161
 
137
- describe "#tag_name" do
138
- it "should extract node tag name" do
162
+ describe '#tag_name' do
163
+ it 'should extract node tag name' do
139
164
  expect(@session.all('//a')[0].tag_name).to eq('a')
140
165
  expect(@session.all('//a')[1].tag_name).to eq('a')
141
166
  expect(@session.all('//p')[1].tag_name).to eq('p')
142
167
  end
143
168
  end
144
169
 
145
- describe "#disabled?" do
146
- it "should extract disabled node" do
170
+ describe '#disabled?' do
171
+ it 'should extract disabled node' do
147
172
  @session.visit('/form')
148
173
  expect(@session.find('//input[@id="customer_name"]')).to be_disabled
149
174
  expect(@session.find('//input[@id="customer_email"]')).not_to be_disabled
150
175
  end
151
176
 
152
- it "should see disabled options as disabled" do
177
+ it 'should see disabled options as disabled' do
153
178
  @session.visit('/form')
154
179
  expect(@session.find('//select[@id="form_title"]/option[1]')).not_to be_disabled
155
180
  expect(@session.find('//select[@id="form_title"]/option[@disabled]')).to be_disabled
156
181
  end
157
182
 
158
- it "should see enabled options in disabled select as disabled" do
183
+ it 'should see enabled options in disabled select as disabled' do
159
184
  @session.visit('/form')
160
185
  expect(@session.find('//select[@id="form_disabled_select"]/option')).to be_disabled
186
+ expect(@session.find('//select[@id="form_disabled_select"]/optgroup/option')).to be_disabled
161
187
  expect(@session.find('//select[@id="form_title"]/option[1]')).not_to be_disabled
162
188
  end
163
189
 
164
- it "should be boolean" do
190
+ it 'should see enabled options in disabled optgroup as disabled' do
191
+ @session.visit('/form')
192
+ expect(@session.find('//option', text: 'A.B.1')).to be_disabled
193
+ expect(@session.find('//option', text: 'A.2')).not_to be_disabled
194
+ end
195
+
196
+ it 'should see a disabled fieldset as disabled' do
197
+ @session.visit('/form')
198
+ expect(@session.find(:xpath, './/fieldset[@id="form_disabled_fieldset"]')).to be_disabled
199
+ end
200
+
201
+ context 'in a disabled fieldset' do
202
+ # https://html.spec.whatwg.org/#the-fieldset-element
203
+ it 'should see elements not in first legend as disabled' do
204
+ @session.visit('/form')
205
+ expect(@session.find('//input[@id="form_disabled_fieldset_child"]')).to be_disabled
206
+ expect(@session.find('//input[@id="form_disabled_fieldset_second_legend_child"]')).to be_disabled
207
+ expect(@session.find('//input[@id="form_enabled_fieldset_child"]')).not_to be_disabled
208
+ end
209
+
210
+ it 'should see elements in first legend as enabled' do
211
+ @session.visit('/form')
212
+ expect(@session.find('//input[@id="form_disabled_fieldset_legend_child"]')).not_to be_disabled
213
+ end
214
+
215
+ it 'should sees options not in first legend as disabled' do
216
+ @session.visit('/form')
217
+ expect(@session.find('//option', text: 'Disabled Child Option')).to be_disabled
218
+ end
219
+ end
220
+
221
+ it 'should be boolean' do
165
222
  @session.visit('/form')
166
223
  expect(@session.find('//select[@id="form_disabled_select"]/option').disabled?).to be true
167
224
  expect(@session.find('//select[@id="form_disabled_select2"]/option').disabled?).to be true
168
225
  expect(@session.find('//select[@id="form_title"]/option[1]').disabled?).to be false
169
226
  end
227
+
228
+ it 'should be disabled for all elements that are CSS :disabled' do
229
+ @session.visit('/form')
230
+ # sanity check
231
+ expect(@session.all(:css, ':disabled')).to all(be_disabled)
232
+ end
170
233
  end
171
234
 
172
- describe "#visible?" do
173
- it "should extract node visibility" do
174
- Capybara.ignore_hidden_elements = false
235
+ describe '#visible?' do
236
+ before { Capybara.ignore_hidden_elements = false }
237
+
238
+ it 'should extract node visibility' do
175
239
  expect(@session.first('//a')).to be_visible
176
240
 
177
241
  expect(@session.find('//div[@id="hidden"]')).not_to be_visible
@@ -181,22 +245,124 @@ Capybara::SpecHelper.spec "node" do
181
245
  expect(@session.find('//input[@id="hidden_input"]')).not_to be_visible
182
246
  end
183
247
 
184
- it "should be boolean" do
185
- Capybara.ignore_hidden_elements = false
248
+ it 'template elements should not be visible' do
249
+ expect(@session.find('//template')).not_to be_visible
250
+ end
251
+
252
+ it 'should be boolean' do
186
253
  expect(@session.first('//a').visible?).to be true
187
254
  expect(@session.find('//div[@id="hidden"]').visible?).to be false
188
255
  end
256
+
257
+ it 'closed details > summary elements and descendants should be visible' do
258
+ expect(@session.find(:css, '#closed_details summary')).to be_visible
259
+ expect(@session.find(:css, '#closed_details summary h6')).to be_visible
260
+ end
261
+
262
+ it 'details non-summary descendants should be non-visible when closed' do
263
+ descendants = @session.all(:css, '#closed_details > *:not(summary), #closed_details > *:not(summary) *', minimum: 2)
264
+ expect(descendants).not_to include(be_visible)
265
+ end
266
+
267
+ it 'deatils descendants should be visible when open' do
268
+ descendants = @session.all(:css, '#open_details *')
269
+ expect(descendants).to all(be_visible)
270
+ end
271
+
272
+ it 'works when details is toggled open and closed' do
273
+ @session.find(:css, '#closed_details > summary').click
274
+ expect(@session).to have_css('#closed_details *', visible: :visible, count: 5)
275
+ .and(have_no_css('#closed_details *', visible: :hidden))
276
+
277
+ @session.find(:css, '#closed_details > summary').click
278
+ descendants_css = '#closed_details > *:not(summary), #closed_details > *:not(summary) *'
279
+ expect(@session).to have_no_css(descendants_css, visible: :visible)
280
+ .and(have_css(descendants_css, visible: :hidden, count: 3))
281
+ end
282
+ end
283
+
284
+ describe '#obscured?', requires: [:css] do
285
+ it 'should see non visible elements as obscured' do
286
+ Capybara.ignore_hidden_elements = false
287
+ expect(@session.find('//div[@id="hidden"]')).to be_obscured
288
+ expect(@session.find('//div[@id="hidden_via_ancestor"]')).to be_obscured
289
+ expect(@session.find('//div[@id="hidden_attr"]')).to be_obscured
290
+ expect(@session.find('//a[@id="hidden_attr_via_ancestor"]')).to be_obscured
291
+ expect(@session.find('//input[@id="hidden_input"]')).to be_obscured
292
+ end
293
+
294
+ it 'should see non-overlapped elements as not obscured' do
295
+ @session.visit('/obscured')
296
+ expect(@session.find(:css, '#cover')).not_to be_obscured
297
+ end
298
+
299
+ it 'should see elements only overlapped by descendants as not obscured' do
300
+ expect(@session.first(:css, 'p:not(.para)')).not_to be_obscured
301
+ end
302
+
303
+ it 'should see elements outside the viewport as obscured' do
304
+ @session.visit('/obscured')
305
+ off = @session.find(:css, '#offscreen')
306
+ off_wrapper = @session.find(:css, '#offscreen_wrapper')
307
+ expect(off).to be_obscured
308
+ expect(off_wrapper).to be_obscured
309
+ @session.scroll_to(off_wrapper)
310
+ expect(off_wrapper).not_to be_obscured
311
+ expect(off).to be_obscured
312
+ off_wrapper.scroll_to(off)
313
+ expect(off).not_to be_obscured
314
+ expect(off_wrapper).not_to be_obscured
315
+ end
316
+
317
+ it 'should see overlapped elements as obscured' do
318
+ @session.visit('/obscured')
319
+ expect(@session.find(:css, '#obscured')).to be_obscured
320
+ end
321
+
322
+ it 'should be boolean' do
323
+ Capybara.ignore_hidden_elements = false
324
+ expect(@session.first('//a').obscured?).to be false
325
+ expect(@session.find('//div[@id="hidden"]').obscured?).to be true
326
+ end
327
+
328
+ it 'should work in frames' do
329
+ @session.visit('/obscured')
330
+ frame = @session.find(:css, '#frameOne')
331
+ @session.within_frame(frame) do
332
+ div = @session.find(:css, '#divInFrameOne')
333
+ expect(div).to be_obscured
334
+ @session.scroll_to div
335
+ expect(div).not_to be_obscured
336
+ end
337
+ end
338
+
339
+ it 'should work in nested iframes' do
340
+ @session.visit('/obscured')
341
+ frame = @session.find(:css, '#nestedFrames')
342
+ @session.within_frame(frame) do
343
+ @session.within_frame(:css, '#childFrame') do
344
+ gcframe = @session.find(:css, '#grandchildFrame2')
345
+ @session.within_frame(gcframe) do
346
+ expect(@session.find(:css, '#divInFrameTwo')).to be_obscured
347
+ end
348
+ @session.scroll_to(gcframe)
349
+ @session.within_frame(gcframe) do
350
+ expect(@session.find(:css, '#divInFrameTwo')).not_to be_obscured
351
+ end
352
+ end
353
+ end
354
+ end
189
355
  end
190
356
 
191
- describe "#checked?" do
192
- it "should extract node checked state" do
357
+ describe '#checked?' do
358
+ it 'should extract node checked state' do
193
359
  @session.visit('/form')
194
360
  expect(@session.find('//input[@id="gender_female"]')).to be_checked
195
361
  expect(@session.find('//input[@id="gender_male"]')).not_to be_checked
196
362
  expect(@session.first('//h1')).not_to be_checked
197
363
  end
198
364
 
199
- it "should be boolean" do
365
+ it 'should be boolean' do
200
366
  @session.visit('/form')
201
367
  expect(@session.find('//input[@id="gender_female"]').checked?).to be true
202
368
  expect(@session.find('//input[@id="gender_male"]').checked?).to be false
@@ -204,15 +370,15 @@ Capybara::SpecHelper.spec "node" do
204
370
  end
205
371
  end
206
372
 
207
- describe "#selected?" do
208
- it "should extract node selected state" do
373
+ describe '#selected?' do
374
+ it 'should extract node selected state' do
209
375
  @session.visit('/form')
210
376
  expect(@session.find('//option[@value="en"]')).to be_selected
211
377
  expect(@session.find('//option[@value="sv"]')).not_to be_selected
212
378
  expect(@session.first('//h1')).not_to be_selected
213
379
  end
214
380
 
215
- it "should be boolean" do
381
+ it 'should be boolean' do
216
382
  @session.visit('/form')
217
383
  expect(@session.find('//option[@value="en"]').selected?).to be true
218
384
  expect(@session.find('//option[@value="sv"]').selected?).to be false
@@ -220,111 +386,825 @@ Capybara::SpecHelper.spec "node" do
220
386
  end
221
387
  end
222
388
 
223
- describe "#==" do
224
- it "preserve object identity" do
225
- expect(@session.find('//h1') == @session.find('//h1')).to be true
226
- expect(@session.find('//h1') === @session.find('//h1')).to be true
227
- expect(@session.find('//h1').eql? @session.find('//h1')).to be false
389
+ describe '#==' do
390
+ it 'preserve object identity' do
391
+ expect(@session.find('//h1') == @session.find('//h1')).to be true # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
392
+ expect(@session.find('//h1') === @session.find('//h1')).to be true # rubocop:disable Style/CaseEquality, Lint/BinaryOperatorWithIdenticalOperands
393
+ expect(@session.find('//h1').eql?(@session.find('//h1'))).to be false
228
394
  end
229
395
 
230
- it "returns false for unrelated object" do
231
- expect(@session.find('//h1') == "Not Capybara::Node::Base").to be false
396
+ it 'returns false for unrelated object' do
397
+ expect(@session.find('//h1') == 'Not Capybara::Node::Base').to be false
232
398
  end
233
399
  end
234
400
 
235
- describe "#path" do
401
+ describe '#path' do
236
402
  # Testing for specific XPaths here doesn't make sense since there
237
403
  # are many that can refer to the same element
238
- before :each do
404
+ before do
239
405
  @session.visit('/path')
240
406
  end
241
407
 
242
- it "returns xpath which points to itself" do
408
+ it 'returns xpath which points to itself' do
243
409
  element = @session.find(:link, 'Second Link')
244
410
  expect(@session.find(:xpath, element.path)).to eq(element)
245
411
  end
412
+
413
+ it 'reports when element in shadow dom', requires: [:shadow_dom] do
414
+ @session.visit('/with_js')
415
+ shadow = @session.find(:css, '#shadow')
416
+ element = @session.evaluate_script(<<~JS, shadow)
417
+ (function(root){
418
+ return root.shadowRoot.querySelector('span');
419
+ })(arguments[0])
420
+ JS
421
+ expect(element.path).to eq '(: Shadow DOM element - no XPath :)'
422
+ end
246
423
  end
247
424
 
248
- describe "#trigger", :requires => [:js, :trigger] do
249
- it "should allow triggering of custom JS events" do
425
+ describe '#trigger', requires: %i[js trigger] do
426
+ it 'should allow triggering of custom JS events' do
250
427
  @session.visit('/with_js')
251
428
  @session.find(:css, '#with_focus_event').trigger(:focus)
252
429
  expect(@session).to have_css('#focus_event_triggered')
253
430
  end
254
431
  end
255
432
 
256
- describe '#drag_to', :requires => [:js, :drag] do
257
- it "should drag and drop an object" do
433
+ describe '#drag_to', requires: %i[js drag] do
434
+ it 'should drag and drop an object' do
258
435
  @session.visit('/with_js')
259
436
  element = @session.find('//div[@id="drag"]')
260
437
  target = @session.find('//div[@id="drop"]')
261
438
  element.drag_to(target)
262
- expect(@session.find('//div[contains(., "Dropped!")]')).not_to be_nil
439
+ expect(@session).to have_xpath('//div[contains(., "Dropped!")]')
440
+ end
441
+
442
+ it 'should drag and drop if scrolling is needed' do
443
+ @session.visit('/with_js')
444
+ element = @session.find('//div[@id="drag_scroll"]')
445
+ target = @session.find('//div[@id="drop_scroll"]')
446
+ element.drag_to(target)
447
+ expect(@session).to have_xpath('//div[contains(., "Dropped!")]')
448
+ end
449
+
450
+ it 'should drag a link' do
451
+ @session.visit('/with_js')
452
+ link = @session.find_link('drag_link')
453
+ target = @session.find(:id, 'drop')
454
+ link.drag_to target
455
+ expect(@session).to have_xpath('//div[contains(., "Dropped!")]')
456
+ end
457
+
458
+ it 'should work with Dragula' do
459
+ @session.visit('/with_dragula')
460
+ @session.within(:css, '#sortable.ready') do
461
+ src = @session.find('div', text: 'Item 1')
462
+ target = @session.find('div', text: 'Item 3')
463
+ src.drag_to target
464
+ expect(@session).to have_content(/Item 2.*Item 1/, normalize_ws: true)
465
+ end
466
+ end
467
+
468
+ it 'should work with jsTree' do
469
+ @session.visit('/with_jstree')
470
+ @session.within(:css, '#container') do
471
+ @session.assert_text(/A.*B.*C/m)
472
+ source = @session.find(:css, '#j1_1_anchor')
473
+ target = @session.find(:css, '#j1_2_anchor')
474
+
475
+ source.drag_to(target)
476
+
477
+ @session.assert_no_text(/A.*B.*C/m)
478
+ @session.assert_text(/B.*C/m)
479
+ end
480
+ end
481
+
482
+ it 'should simulate a single held down modifier key' do
483
+ %I[
484
+ alt
485
+ ctrl
486
+ meta
487
+ shift
488
+ ].each do |modifier_key|
489
+ @session.visit('/with_js')
490
+
491
+ element = @session.find('//div[@id="drag"]')
492
+ target = @session.find('//div[@id="drop"]')
493
+
494
+ element.drag_to(target, drop_modifiers: modifier_key)
495
+ expect(@session).to have_css('div.drag_start', exact_text: 'Dragged!')
496
+ expect(@session).to have_xpath("//div[contains(., 'Dropped!-#{modifier_key}')]")
497
+ end
498
+ end
499
+
500
+ it 'should simulate multiple held down modifier keys' do
501
+ @session.visit('/with_js')
502
+
503
+ element = @session.find('//div[@id="drag"]')
504
+ target = @session.find('//div[@id="drop"]')
505
+
506
+ modifier_keys = %I[alt ctrl meta shift]
507
+
508
+ element.drag_to(target, drop_modifiers: modifier_keys)
509
+ expect(@session).to have_xpath("//div[contains(., 'Dropped!-#{modifier_keys.join('-')}')]")
510
+ end
511
+
512
+ it 'should support key aliases' do
513
+ { control: :ctrl,
514
+ command: :meta,
515
+ cmd: :meta }.each do |(key_alias, key)|
516
+ @session.visit('/with_js')
517
+
518
+ element = @session.find('//div[@id="drag"]')
519
+ target = @session.find('//div[@id="drop"]')
520
+
521
+ element.drag_to(target, drop_modifiers: [key_alias])
522
+ expect(target).to have_text("Dropped!-#{key}", exact: true)
523
+ end
524
+ end
525
+
526
+ context 'HTML5', requires: %i[js html5_drag] do
527
+ it 'should HTML5 drag and drop an object' do
528
+ @session.visit('/with_js')
529
+ element = @session.find('//div[@id="drag_html5"]')
530
+ target = @session.find('//div[@id="drop_html5"]')
531
+ element.drag_to(target)
532
+ expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped string: text/plain drag_html5")]')
533
+ end
534
+
535
+ it 'should HTML5 drag and drop an object child' do
536
+ @session.visit('/with_js')
537
+ element = @session.find('//div[@id="drag_html5"]/p')
538
+ target = @session.find('//div[@id="drop_html5"]')
539
+ element.drag_to(target)
540
+ expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped string: text/plain drag_html5")]')
541
+ end
542
+
543
+ it 'should set clientX/Y in dragover events' do
544
+ @session.visit('/with_js')
545
+ element = @session.find('//div[@id="drag_html5"]')
546
+ target = @session.find('//div[@id="drop_html5"]')
547
+ element.drag_to(target)
548
+ expect(@session).to have_css('div.log', text: /DragOver with client position: [1-9]\d*,[1-9]\d*/, count: 2)
549
+ end
550
+
551
+ it 'should preserve clientX/Y from last dragover event' do
552
+ @session.visit('/with_js')
553
+ element = @session.find('//div[@id="drag_html5"]')
554
+ target = @session.find('//div[@id="drop_html5"]')
555
+ element.drag_to(target)
556
+
557
+ conditions = %w[DragLeave Drop DragEnd].map do |text|
558
+ have_css('div.log', text: text)
559
+ end
560
+ expect(@session).to(conditions.reduce { |memo, cond| memo.and(cond) })
561
+
562
+ # The first "DragOver" div is inserted by the last dragover event dispatched
563
+ drag_over_div = @session.first('//div[@class="log" and starts-with(text(), "DragOver")]')
564
+ position = drag_over_div.text.sub('DragOver ', '')
565
+
566
+ expect(@session).to have_css('div.log', text: /DragLeave #{position}/, count: 1)
567
+ expect(@session).to have_css('div.log', text: /Drop #{position}/, count: 1)
568
+ expect(@session).to have_css('div.log', text: /DragEnd #{position}/, count: 1)
569
+ end
570
+
571
+ it 'should not HTML5 drag and drop on a non HTML5 drop element' do
572
+ @session.visit('/with_js')
573
+ element = @session.find('//div[@id="drag_html5"]')
574
+ target = @session.find('//div[@id="drop_html5"]')
575
+ target.execute_script("$(this).removeClass('drop');")
576
+ element.drag_to(target)
577
+ sleep 1
578
+ expect(@session).not_to have_xpath('//div[contains(., "HTML5 Dropped")]')
579
+ end
580
+
581
+ it 'should HTML5 drag and drop when scrolling needed' do
582
+ @session.visit('/with_js')
583
+ element = @session.find('//div[@id="drag_html5_scroll"]')
584
+ target = @session.find('//div[@id="drop_html5_scroll"]')
585
+ element.drag_to(target)
586
+ expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped string: text/plain drag_html5_scroll")]')
587
+ end
588
+
589
+ it 'should drag HTML5 default draggable elements' do
590
+ @session.visit('/with_js')
591
+ link = @session.find_link('drag_link_html5')
592
+ target = @session.find(:id, 'drop_html5')
593
+ link.drag_to target
594
+ expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped")]')
595
+ end
596
+
597
+ it 'should work with SortableJS' do
598
+ @session.visit('/with_sortable_js')
599
+ @session.within(:css, '#sortable') do
600
+ src = @session.find('div', text: 'Item 1')
601
+ target = @session.find('div', text: 'Item 3')
602
+ src.drag_to target
603
+ expect(@session).to have_content(/Item 3.*Item 1/, normalize_ws: true)
604
+ end
605
+ end
606
+
607
+ it 'should drag HTML5 default draggable element child' do
608
+ @session.visit('/with_js')
609
+ source = @session.find_link('drag_link_html5').find(:css, 'p')
610
+ target = @session.find(:id, 'drop_html5')
611
+ source.drag_to target
612
+ expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped")]')
613
+ end
614
+
615
+ it 'should simulate a single held down modifier key' do
616
+ %I[alt ctrl meta shift].each do |modifier_key|
617
+ @session.visit('/with_js')
618
+
619
+ element = @session.find('//div[@id="drag_html5"]')
620
+ target = @session.find('//div[@id="drop_html5"]')
621
+
622
+ element.drag_to(target, drop_modifiers: modifier_key)
623
+
624
+ expect(@session).to have_css('div.drag_start', exact_text: 'HTML5 Dragged!')
625
+ expect(@session).to have_xpath("//div[contains(., 'HTML5 Dropped string: text/plain drag_html5-#{modifier_key}')]")
626
+ end
627
+ end
628
+
629
+ it 'should simulate multiple held down modifier keys' do
630
+ @session.visit('/with_js')
631
+
632
+ element = @session.find('//div[@id="drag_html5"]')
633
+ target = @session.find('//div[@id="drop_html5"]')
634
+
635
+ modifier_keys = %I[alt ctrl meta shift]
636
+
637
+ element.drag_to(target, drop_modifiers: modifier_keys)
638
+ expect(@session).to have_xpath("//div[contains(., 'HTML5 Dropped string: text/plain drag_html5-#{modifier_keys.join('-')}')]")
639
+ end
640
+
641
+ it 'should support key aliases' do
642
+ { control: :ctrl,
643
+ command: :meta,
644
+ cmd: :meta }.each do |(key_alias, key)|
645
+ @session.visit('/with_js')
646
+
647
+ element = @session.find('//div[@id="drag_html5"]')
648
+ target = @session.find('//div[@id="drop_html5"]')
649
+
650
+ element.drag_to(target, drop_modifiers: [key_alias])
651
+ expect(target).to have_text(%r{^HTML5 Dropped string: text/plain drag_html5-#{key}$}m, exact: true)
652
+ end
653
+ end
654
+ end
655
+ end
656
+
657
+ describe 'Element#drop', requires: %i[js html5_drag] do
658
+ it 'can drop a file' do
659
+ @session.visit('/with_js')
660
+ target = @session.find('//div[@id="drop_html5"]')
661
+ target.drop(
662
+ with_os_path_separators(File.expand_path('../fixtures/capybara.jpg', File.dirname(__FILE__)))
663
+ )
664
+ expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped file: capybara.jpg")]')
665
+ end
666
+
667
+ it 'can drop multiple files' do
668
+ @session.visit('/with_js')
669
+ target = @session.find('//div[@id="drop_html5"]')
670
+ target.drop(
671
+ with_os_path_separators(File.expand_path('../fixtures/capybara.jpg', File.dirname(__FILE__))),
672
+ with_os_path_separators(File.expand_path('../fixtures/test_file.txt', File.dirname(__FILE__)))
673
+ )
674
+ expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped file: capybara.jpg")]')
675
+ expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped file: test_file.txt")]')
676
+ end
677
+
678
+ it 'can drop strings' do
679
+ @session.visit('/with_js')
680
+ target = @session.find('//div[@id="drop_html5"]')
681
+ target.drop('text/plain' => 'Some dropped text')
682
+ expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped string: text/plain Some dropped text")]')
683
+ end
684
+
685
+ it 'can drop a pathname' do
686
+ @session.visit('/with_js')
687
+ target = @session.find('//div[@id="drop_html5"]')
688
+ target.drop(
689
+ Pathname.new(with_os_path_separators(File.expand_path('../fixtures/capybara.jpg', File.dirname(__FILE__))))
690
+ )
691
+ expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped file: capybara.jpg")]')
692
+ end
693
+
694
+ it 'can drop multiple strings' do
695
+ @session.visit('/with_js')
696
+ target = @session.find('//div[@id="drop_html5"]')
697
+ target.drop('text/plain' => 'Some dropped text', 'text/url' => 'http://www.google.com')
698
+ expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped string: text/plain Some dropped text")]')
699
+ expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped string: text/url http://www.google.com")]')
263
700
  end
264
701
  end
265
702
 
266
- describe '#hover', :requires => [:hover] do
267
- it "should allow hovering on an element" do
703
+ describe '#hover', requires: [:hover] do
704
+ it 'should allow hovering on an element' do
268
705
  @session.visit('/with_hover')
269
- expect(@session.find(:css,'.hidden_until_hover', :visible => false)).not_to be_visible
270
- @session.find(:css,'.wrapper').hover
271
- expect(@session.find(:css, '.hidden_until_hover', :visible => false)).to be_visible
706
+ expect(@session.find(:css, '.wrapper:not(.scroll_needed) .hidden_until_hover', visible: false)).not_to be_visible
707
+ @session.find(:css, '.wrapper:not(.scroll_needed)').hover
708
+ expect(@session.find(:css, '.wrapper:not(.scroll_needed) .hidden_until_hover', visible: false)).to be_visible
709
+ end
710
+
711
+ it 'should allow hovering on an element that needs to be scrolled into view' do
712
+ @session.visit('/with_hover')
713
+ expect(@session.find(:css, '.wrapper.scroll_needed .hidden_until_hover', visible: false)).not_to be_visible
714
+ @session.find(:css, '.wrapper.scroll_needed').hover
715
+ expect(@session.find(:css, '.wrapper.scroll_needed .hidden_until_hover', visible: false)).to be_visible
716
+ end
717
+
718
+ it 'should hover again after following a link and back' do
719
+ @session.visit('/with_hover')
720
+ @session.find(:css, '.wrapper:not(.scroll_needed)').hover
721
+ @session.click_link('Other hover page')
722
+ @session.click_link('Go back')
723
+ @session.find(:css, '.wrapper:not(.scroll_needed)').hover
724
+ expect(@session.find(:css, '.wrapper:not(.scroll_needed) .hidden_until_hover', visible: false)).to be_visible
272
725
  end
273
726
  end
274
727
 
275
728
  describe '#click' do
276
- it "should not follow a link if no href" do
729
+ it 'should not follow a link if no href' do
277
730
  @session.find(:css, '#link_placeholder').click
278
731
  expect(@session.current_url).to match(%r{/with_html$})
279
732
  end
733
+
734
+ it 'should go to the same page if href is blank' do
735
+ @session.find(:css, '#link_blank_href').click
736
+ sleep 1
737
+ expect(@session).to have_current_path('/with_html')
738
+ end
739
+
740
+ it 'should be able to check a checkbox' do
741
+ @session.visit('form')
742
+ cbox = @session.find(:checkbox, 'form_terms_of_use')
743
+ expect(cbox).not_to be_checked
744
+ cbox.click
745
+ expect(cbox).to be_checked
746
+ end
747
+
748
+ it 'should be able to uncheck a checkbox' do
749
+ @session.visit('/form')
750
+ cbox = @session.find(:checkbox, 'form_pets_dog')
751
+ expect(cbox).to be_checked
752
+ cbox.click
753
+ expect(cbox).not_to be_checked
754
+ end
755
+
756
+ it 'should be able to select a radio button' do
757
+ @session.visit('/form')
758
+ radio = @session.find(:radio_button, 'gender_male')
759
+ expect(radio).not_to be_checked
760
+ radio.click
761
+ expect(radio).to be_checked
762
+ end
763
+
764
+ it 'should allow modifiers', requires: [:js] do
765
+ @session.visit('/with_js')
766
+ @session.find(:css, '#click-test').click(:shift)
767
+ expect(@session).to have_link('Has been shift clicked')
768
+ end
769
+
770
+ it 'should allow multiple modifiers', requires: [:js] do
771
+ @session.visit('with_js')
772
+ @session.find(:css, '#click-test').click(:control, :alt, :meta, :shift)
773
+ # Selenium with Chrome on OSX ctrl-click generates a right click so just verify all keys but not click type
774
+ expect(@session).to have_link('alt control meta shift')
775
+ end
776
+
777
+ it 'should allow to adjust the click offset', requires: [:js] do
778
+ @session.visit('with_js')
779
+ @session.find(:css, '#click-test').click(x: 5, y: 5)
780
+ link = @session.find(:link, 'has-been-clicked')
781
+ locations = link.text.match(/^Has been clicked at (?<x>[\d.-]+),(?<y>[\d.-]+)$/)
782
+ # Resulting click location should be very close to 0, 0 relative to top left corner of the element, but may not be exact due to
783
+ # integer/float conversions and rounding.
784
+ expect(locations[:x].to_f).to be_within(1).of(5)
785
+ expect(locations[:y].to_f).to be_within(1).of(5)
786
+ end
787
+
788
+ it 'should raise error if both x and y values are not passed' do
789
+ @session.visit('with_js')
790
+ el = @session.find(:css, '#click-test')
791
+ expect { el.click(x: 5) }.to raise_error ArgumentError
792
+ expect { el.click(x: nil, y: 3) }.to raise_error ArgumentError
793
+ end
794
+
795
+ it 'should be able to click a table row', requires: [:js] do
796
+ @session.visit('/tables')
797
+ tr = @session.find(:css, '#agent_table tr:first-child').click
798
+ expect(tr).to have_css('label', text: 'Clicked')
799
+ end
800
+
801
+ it 'should retry clicking', requires: [:js] do
802
+ @session.visit('/animated')
803
+ obscured = @session.find(:css, '#obscured')
804
+ @session.execute_script <<~JS
805
+ setTimeout(function(){ $('#cover').hide(); }, 700)
806
+ JS
807
+ expect { obscured.click }.not_to raise_error
808
+ end
809
+
810
+ it 'should allow to retry longer', requires: [:js] do
811
+ @session.visit('/animated')
812
+ obscured = @session.find(:css, '#obscured')
813
+ @session.execute_script <<~JS
814
+ setTimeout(function(){ $('#cover').hide(); }, 3000)
815
+ JS
816
+ expect { obscured.click(wait: 4) }.not_to raise_error
817
+ end
818
+
819
+ it 'should not retry clicking when wait is disabled', requires: [:js] do
820
+ @session.visit('/animated')
821
+ obscured = @session.find(:css, '#obscured')
822
+ @session.execute_script <<~JS
823
+ setTimeout(function(){ $('#cover').hide(); }, 2000)
824
+ JS
825
+ expect { obscured.click(wait: 0) }.to(raise_error { |e| expect(e).to be_an_invalid_element_error(@session) })
826
+ end
827
+
828
+ context 'offset', requires: [:js] do
829
+ before do
830
+ @session.visit('/offset')
831
+ end
832
+
833
+ let :clicker do
834
+ @session.find(:id, 'clicker')
835
+ end
836
+
837
+ context 'when w3c_click_offset is false' do
838
+ before do
839
+ Capybara.w3c_click_offset = false
840
+ end
841
+
842
+ it 'should offset from top left of element' do
843
+ clicker.click(x: 10, y: 5)
844
+ expect(@session).to have_text(/clicked at 110,105/)
845
+ end
846
+
847
+ it 'should offset outside the element' do
848
+ clicker.click(x: -15, y: -10)
849
+ expect(@session).to have_text(/clicked at 85,90/)
850
+ end
851
+
852
+ it 'should default to click the middle' do
853
+ clicker.click
854
+ expect(@session).to have_text(/clicked at 150,150/)
855
+ end
856
+ end
857
+
858
+ context 'when w3c_click_offset is true' do
859
+ before do
860
+ Capybara.w3c_click_offset = true
861
+ end
862
+
863
+ it 'should offset from center of element' do
864
+ clicker.click(x: 10, y: 5)
865
+ expect(@session).to have_text(/clicked at 160,155/)
866
+ end
867
+
868
+ it 'should offset outside from center of element' do
869
+ clicker.click(x: -65, y: -60)
870
+ expect(@session).to have_text(/clicked at 85,90/)
871
+ end
872
+
873
+ it 'should default to click the middle' do
874
+ clicker.click
875
+ expect(@session).to have_text(/clicked at 150,150/)
876
+ end
877
+ end
878
+ end
879
+
880
+ context 'delay', requires: [:js] do
881
+ it 'should delay the mouse up' do
882
+ @session.visit('with_js')
883
+ @session.find(:css, '#click-test').click(delay: 2)
884
+ delay = @session.evaluate_script('window.click_delay')
885
+ expect(delay).to be >= 2
886
+ end
887
+ end
280
888
  end
281
889
 
282
- describe '#double_click', :requires => [:js] do
283
- it "should double click an element" do
890
+ describe '#double_click', requires: [:js] do
891
+ it 'should double click an element' do
284
892
  @session.visit('/with_js')
285
893
  @session.find(:css, '#click-test').double_click
286
- expect(@session.find(:css, '#has-been-double-clicked')).to be
894
+ expect(@session.find(:css, '#has-been-double-clicked')).to be_truthy
895
+ end
896
+
897
+ it 'should allow modifiers', requires: [:js] do
898
+ @session.visit('/with_js')
899
+ @session.find(:css, '#click-test').double_click(:alt)
900
+ expect(@session).to have_link('Has been alt double clicked')
901
+ end
902
+
903
+ it 'should allow to adjust the offset', requires: [:js] do
904
+ @session.visit('with_js')
905
+ @session.find(:css, '#click-test').double_click(x: 10, y: 5)
906
+ link = @session.find(:link, 'has-been-double-clicked')
907
+ locations = link.text.match(/^Has been double clicked at (?<x>[\d.-]+),(?<y>[\d.-]+)$/)
908
+ # Resulting click location should be very close to 10, 5 relative to top left corner of the element, but may not be exact due
909
+ # to integer/float conversions and rounding.
910
+ expect(locations[:x].to_f).to be_within(1).of(10)
911
+ expect(locations[:y].to_f).to be_within(1).of(5)
912
+ end
913
+
914
+ it 'should retry clicking', requires: [:js] do
915
+ @session.visit('/animated')
916
+ obscured = @session.find(:css, '#obscured')
917
+ @session.execute_script <<~JS
918
+ setTimeout(function(){ $('#cover').hide(); }, 700)
919
+ JS
920
+ expect { obscured.double_click }.not_to raise_error
921
+ end
922
+
923
+ context 'offset', requires: [:js] do
924
+ before do
925
+ @session.visit('/offset')
926
+ end
927
+
928
+ let :clicker do
929
+ @session.find(:id, 'clicker')
930
+ end
931
+
932
+ context 'when w3c_click_offset is false' do
933
+ before do
934
+ Capybara.w3c_click_offset = false
935
+ end
936
+
937
+ it 'should offset from top left of element' do
938
+ clicker.double_click(x: 10, y: 5)
939
+ expect(@session).to have_text(/clicked at 110,105/)
940
+ end
941
+
942
+ it 'should offset outside the element' do
943
+ clicker.double_click(x: -15, y: -10)
944
+ expect(@session).to have_text(/clicked at 85,90/)
945
+ end
946
+
947
+ it 'should default to click the middle' do
948
+ clicker.double_click
949
+ expect(@session).to have_text(/clicked at 150,150/)
950
+ end
951
+ end
952
+
953
+ context 'when w3c_click_offset is true' do
954
+ before do
955
+ Capybara.w3c_click_offset = true
956
+ end
957
+
958
+ it 'should offset from center of element' do
959
+ clicker.double_click(x: 10, y: 5)
960
+ expect(@session).to have_text(/clicked at 160,155/)
961
+ end
962
+
963
+ it 'should offset outside from center of element' do
964
+ clicker.double_click(x: -65, y: -60)
965
+ expect(@session).to have_text(/clicked at 85,90/)
966
+ end
967
+
968
+ it 'should default to click the middle' do
969
+ clicker.double_click
970
+ expect(@session).to have_text(/clicked at 150,150/)
971
+ end
972
+ end
287
973
  end
288
974
  end
289
975
 
290
- describe '#right_click', :requires => [:js] do
291
- it "should right click an element" do
976
+ describe '#right_click', requires: [:js] do
977
+ it 'should right click an element' do
292
978
  @session.visit('/with_js')
293
979
  @session.find(:css, '#click-test').right_click
294
- expect(@session.find(:css, '#has-been-right-clicked')).to be
980
+ expect(@session.find(:css, '#has-been-right-clicked')).to be_truthy
981
+ end
982
+
983
+ it 'should allow modifiers', requires: [:js] do
984
+ @session.visit('/with_js')
985
+ @session.find(:css, '#click-test').right_click(:meta)
986
+ expect(@session).to have_link('Has been meta right clicked')
987
+ end
988
+
989
+ it 'should allow to adjust the offset', requires: [:js] do
990
+ @session.visit('with_js')
991
+ @session.find(:css, '#click-test').right_click(x: 10, y: 10)
992
+ link = @session.find(:link, 'has-been-right-clicked')
993
+ locations = link.text.match(/^Has been right clicked at (?<x>[\d.-]+),(?<y>[\d.-]+)$/)
994
+ # Resulting click location should be very close to 10, 10 relative to top left corner of the element, but may not be exact due
995
+ # to integer/float conversions and rounding
996
+ expect(locations[:x].to_f).to be_within(1).of(10)
997
+ expect(locations[:y].to_f).to be_within(1).of(10)
998
+ end
999
+
1000
+ it 'should retry clicking', requires: [:js] do
1001
+ @session.visit('/animated')
1002
+ obscured = @session.find(:css, '#obscured')
1003
+ @session.execute_script <<~JS
1004
+ setTimeout(function(){ $('#cover').hide(); }, 700)
1005
+ JS
1006
+ expect { obscured.right_click }.not_to raise_error
1007
+ end
1008
+
1009
+ context 'offset', requires: [:js] do
1010
+ before do
1011
+ @session.visit('/offset')
1012
+ end
1013
+
1014
+ let :clicker do
1015
+ @session.find(:id, 'clicker')
1016
+ end
1017
+
1018
+ context 'when w3c_click_offset is false' do
1019
+ before do
1020
+ Capybara.w3c_click_offset = false
1021
+ end
1022
+
1023
+ it 'should offset from top left of element' do
1024
+ clicker.right_click(x: 10, y: 5)
1025
+ expect(@session).to have_text(/clicked at 110,105/)
1026
+ end
1027
+
1028
+ it 'should offset outside the element' do
1029
+ clicker.right_click(x: -15, y: -10)
1030
+ expect(@session).to have_text(/clicked at 85,90/)
1031
+ end
1032
+
1033
+ it 'should default to click the middle' do
1034
+ clicker.right_click
1035
+ expect(@session).to have_text(/clicked at 150,150/)
1036
+ end
1037
+ end
1038
+
1039
+ context 'when w3c_click_offset is true' do
1040
+ before do
1041
+ Capybara.w3c_click_offset = true
1042
+ end
1043
+
1044
+ it 'should offset from center of element' do
1045
+ clicker.right_click(x: 10, y: 5)
1046
+ expect(@session).to have_text(/clicked at 160,155/)
1047
+ end
1048
+
1049
+ it 'should offset outside from center of element' do
1050
+ clicker.right_click(x: -65, y: -60)
1051
+ expect(@session).to have_text(/clicked at 85,90/)
1052
+ end
1053
+
1054
+ it 'should default to click the middle' do
1055
+ clicker.right_click
1056
+ expect(@session).to have_text(/clicked at 150,150/)
1057
+ end
1058
+ end
1059
+ end
1060
+
1061
+ context 'delay', requires: [:js] do
1062
+ it 'should delay the mouse up' do
1063
+ @session.visit('with_js')
1064
+ @session.find(:css, '#click-test').right_click(delay: 2)
1065
+ delay = @session.evaluate_script('window.right_click_delay')
1066
+ expect(delay).to be >= 2
1067
+ end
295
1068
  end
296
1069
  end
297
1070
 
298
1071
  describe '#send_keys', requires: [:send_keys] do
299
- it "should send a string of keys to an element" do
1072
+ it 'should send a string of keys to an element' do
300
1073
  @session.visit('/form')
301
1074
  @session.find(:css, '#address1_city').send_keys('Oceanside')
302
1075
  expect(@session.find(:css, '#address1_city').value).to eq 'Oceanside'
303
1076
  end
304
1077
 
305
- it "should send special characters" do
1078
+ it 'should send special characters' do
306
1079
  @session.visit('/form')
307
1080
  @session.find(:css, '#address1_city').send_keys('Ocean', :space, 'sie', :left, 'd')
308
1081
  expect(@session.find(:css, '#address1_city').value).to eq 'Ocean side'
309
1082
  end
310
1083
 
311
- it "should allow for multiple simultaneous keys" do
1084
+ it 'should allow for multiple simultaneous keys' do
312
1085
  @session.visit('/form')
313
1086
  @session.find(:css, '#address1_city').send_keys([:shift, 'o'], 'ceanside')
314
1087
  expect(@session.find(:css, '#address1_city').value).to eq 'Oceanside'
315
1088
  end
316
1089
 
317
- it "should generate key events", requires: [:send_keys, :js] do
1090
+ it 'should hold modifiers at top level' do
1091
+ @session.visit('/form')
1092
+ @session.find(:css, '#address1_city').send_keys('ocean', :shift, 'side')
1093
+ expect(@session.find(:css, '#address1_city').value).to eq 'oceanSIDE'
1094
+ end
1095
+
1096
+ it 'should generate key events', requires: %i[send_keys js] do
318
1097
  @session.visit('/with_js')
319
- @session.find(:css, '#with-key-events').send_keys([:shift,'t'], [:shift,'w'])
1098
+ @session.find(:css, '#with-key-events').send_keys([:shift, 't'], [:shift, 'w'])
320
1099
  expect(@session.find(:css, '#key-events-output')).to have_text('keydown:16 keydown:84 keydown:16 keydown:87')
321
1100
  end
322
1101
  end
323
1102
 
324
- describe '#reload', :requires => [:js] do
325
- context "without automatic reload" do
1103
+ describe '#execute_script', requires: %i[js es_args] do
1104
+ it 'should execute the given script in the context of the element and return nothing' do
1105
+ @session.visit('/with_js')
1106
+ expect(@session.find(:css, '#change').execute_script("this.textContent = 'Funky Doodle'")).to be_nil
1107
+ expect(@session).to have_css('#change', text: 'Funky Doodle')
1108
+ end
1109
+
1110
+ it 'should pass arguments to the script' do
1111
+ @session.visit('/with_js')
1112
+ @session.find(:css, '#change').execute_script('this.textContent = arguments[0]', 'Doodle Funk')
1113
+ expect(@session).to have_css('#change', text: 'Doodle Funk')
1114
+ end
1115
+ end
1116
+
1117
+ describe '#evaluate_script', requires: %i[js es_args] do
1118
+ it 'should evaluate the given script in the context of the element and return whatever it produces' do
1119
+ @session.visit('/with_js')
1120
+ el = @session.find(:css, '#with_change_event')
1121
+ expect(el.evaluate_script('this.value')).to eq('default value')
1122
+ end
1123
+
1124
+ it 'should ignore leading whitespace' do
1125
+ @session.visit('/with_js')
1126
+ expect(@session.find(:css, '#change').evaluate_script('
1127
+ 2 + 3
1128
+ ')).to eq 5
1129
+ end
1130
+
1131
+ it 'should pass arguments to the script' do
1132
+ @session.visit('/with_js')
1133
+ @session.find(:css, '#change').evaluate_script('this.textContent = arguments[0]', 'Doodle Funk')
1134
+ expect(@session).to have_css('#change', text: 'Doodle Funk')
1135
+ end
1136
+
1137
+ it 'should pass multiple arguments' do
1138
+ @session.visit('/with_js')
1139
+ change = @session.find(:css, '#change')
1140
+ expect(change.evaluate_script('arguments[0] + arguments[1]', 2, 3)).to eq 5
1141
+ end
1142
+
1143
+ it 'should support returning elements' do
1144
+ @session.visit('/with_js')
1145
+ change = @session.find(:css, '#change') # ensure page has loaded and element is available
1146
+ el = change.evaluate_script('this')
1147
+ expect(el).to be_instance_of(Capybara::Node::Element)
1148
+ expect(el).to eq(change)
1149
+ end
1150
+
1151
+ it 'should support multiple statements via IIFE' do
1152
+ @session.visit('/with_js')
1153
+ change = @session.find(:css, '#change') # ensure page has loaded and element is available
1154
+ res = change.evaluate_script(<<~JS, 3)
1155
+ (function(n){
1156
+ var el = this;
1157
+ return [el, n];
1158
+ }).apply(this, arguments)
1159
+ JS
1160
+ expect(res).to eq [change, 3]
1161
+ end
1162
+ end
1163
+
1164
+ describe '#evaluate_async_script', requires: %i[js es_args] do
1165
+ it 'should evaluate the given script in the context of the element' do
1166
+ @session.visit('/with_js')
1167
+ el = @session.find(:css, '#with_change_event')
1168
+ expect(el.evaluate_async_script('arguments[0](this.value)')).to eq('default value')
1169
+ end
1170
+
1171
+ it 'should support returning elements after asynchronous operation' do
1172
+ @session.visit('/with_js')
1173
+ change = @session.find(:css, '#change') # ensure page has loaded and element is available
1174
+ el = change.evaluate_async_script('var cb = arguments[0]; setTimeout(function(el){ cb(el) }, 100, this)')
1175
+ expect(el).to eq(change)
1176
+ end
1177
+ end
1178
+
1179
+ describe '#reload', requires: [:js] do
1180
+ it 'should reload elements found via ancestor with CSS' do
1181
+ @session.visit('/with_js')
1182
+ node = @session.find(:css, '#reload-me em').ancestor(:css, 'div')
1183
+ node.reload
1184
+ expect(node[:id]).to eq 'reload-me'
1185
+ end
1186
+
1187
+ it 'should reload elements found via ancestor with XPath' do
1188
+ @session.visit('/with_js')
1189
+ node = @session.find(:css, '#reload-me em').ancestor(:xpath, './/div')
1190
+ node.reload
1191
+ expect(node[:id]).to eq 'reload-me'
1192
+ end
1193
+
1194
+ it 'should reload elements found via sibling' do
1195
+ @session.visit('/with_js')
1196
+ node = @session.find(:css, '#the-list li', text: 'Item 1').sibling(:css, 'li')
1197
+ expect(node.text).to eq 'Item 2'
1198
+ node.reload
1199
+ expect(node.text).to eq 'Item 2'
1200
+ end
1201
+
1202
+ context 'without automatic reload' do
326
1203
  before { Capybara.automatic_reload = false }
327
- it "should reload the current context of the node" do
1204
+
1205
+ after { Capybara.automatic_reload = true }
1206
+
1207
+ it 'should reload the current context of the node' do
328
1208
  @session.visit('/with_js')
329
1209
  node = @session.find(:css, '#reload-me')
330
1210
  @session.click_link('Reload!')
@@ -333,7 +1213,7 @@ Capybara::SpecHelper.spec "node" do
333
1213
  expect(node.text).to eq('has been reloaded')
334
1214
  end
335
1215
 
336
- it "should reload a parent node" do
1216
+ it 'should reload a parent node' do
337
1217
  @session.visit('/with_js')
338
1218
  node = @session.find(:css, '#reload-me').find(:css, 'em')
339
1219
  @session.click_link('Reload!')
@@ -342,42 +1222,45 @@ Capybara::SpecHelper.spec "node" do
342
1222
  expect(node.text).to eq('has been reloaded')
343
1223
  end
344
1224
 
345
- it "should not automatically reload" do
1225
+ it 'should not automatically reload' do
346
1226
  @session.visit('/with_js')
347
1227
  node = @session.find(:css, '#reload-me')
348
1228
  @session.click_link('Reload!')
349
1229
  sleep(0.3)
350
1230
  expect do
351
1231
  expect(node).to have_text('has been reloaded')
352
- end.to raise_error do |error|
1232
+ end.to(raise_error do |error|
353
1233
  expect(error).to be_an_invalid_element_error(@session)
354
- end
1234
+ end)
355
1235
  end
356
- after { Capybara.automatic_reload = true }
357
1236
  end
358
1237
 
359
- context "with automatic reload" do
360
- it "should reload the current context of the node automatically" do
1238
+ context 'with automatic reload' do
1239
+ before do
1240
+ Capybara.default_max_wait_time = 4
1241
+ end
1242
+
1243
+ it 'should reload the current context of the node automatically' do
361
1244
  @session.visit('/with_js')
362
1245
  node = @session.find(:css, '#reload-me')
363
1246
  @session.click_link('Reload!')
364
- sleep(0.3)
1247
+ sleep(1)
365
1248
  expect(node.text).to eq('has been reloaded')
366
1249
  end
367
1250
 
368
- it "should reload a parent node automatically" do
1251
+ it 'should reload a parent node automatically' do
369
1252
  @session.visit('/with_js')
370
1253
  node = @session.find(:css, '#reload-me').find(:css, 'em')
371
1254
  @session.click_link('Reload!')
372
- sleep(0.3)
1255
+ sleep(1)
373
1256
  expect(node.text).to eq('has been reloaded')
374
1257
  end
375
1258
 
376
- it "should reload a node automatically when using find" do
1259
+ it 'should reload a node automatically when using find' do
377
1260
  @session.visit('/with_js')
378
1261
  node = @session.find(:css, '#reload-me')
379
1262
  @session.click_link('Reload!')
380
- sleep(0.3)
1263
+ sleep(1)
381
1264
  expect(node.find(:css, 'a').text).to eq('has been reloaded')
382
1265
  end
383
1266
 
@@ -385,23 +1268,19 @@ Capybara::SpecHelper.spec "node" do
385
1268
  @session.visit('/with_js')
386
1269
  node = @session.all(:css, '#the-list li')[1]
387
1270
  @session.click_link('Fetch new list!')
388
- sleep(0.3)
1271
+ sleep(1)
389
1272
 
390
1273
  expect do
391
1274
  expect(node).to have_text('Foo')
392
- end.to raise_error { |error|
393
- expect(error).to be_an_invalid_element_error(@session)
394
- }
1275
+ end.to(raise_error { |error| expect(error).to be_an_invalid_element_error(@session) })
395
1276
  expect do
396
1277
  expect(node).to have_text('Bar')
397
- end.to raise_error { |error|
398
- expect(error).to be_an_invalid_element_error(@session)
399
- }
1278
+ end.to(raise_error { |error| expect(error).to be_an_invalid_element_error(@session) })
400
1279
  end
401
1280
 
402
- it "should reload nodes with options" do
1281
+ it 'should reload nodes with options' do
403
1282
  @session.visit('/with_js')
404
- node = @session.find(:css, 'em', :text => "reloaded")
1283
+ node = @session.find(:css, 'em', text: 'reloaded')
405
1284
  @session.click_link('Reload!')
406
1285
  sleep(1)
407
1286
  expect(node.text).to eq('has been reloaded')
@@ -409,7 +1288,25 @@ Capybara::SpecHelper.spec "node" do
409
1288
  end
410
1289
  end
411
1290
 
412
- def be_an_invalid_element_error(session)
413
- satisfy { |error| session.driver.invalid_element_errors.any? { |e| error.is_a? e } }
1291
+ context 'when #synchronize raises server errors' do
1292
+ it 'sets an explanatory exception as the cause of server exceptions', requires: %i[server js] do
1293
+ quietly { @session.visit('/error') }
1294
+ expect do
1295
+ @session.find(:css, 'span')
1296
+ end.to(raise_error(TestApp::TestAppError) do |e|
1297
+ expect(e.cause).to be_a Capybara::CapybaraError
1298
+ expect(e.cause.message).to match(/Your application server raised an error/)
1299
+ end)
1300
+ end
1301
+
1302
+ it 'sets an explanatory exception as the cause of server exceptions with errors with initializers', requires: %i[server js] do
1303
+ quietly { @session.visit('/other_error') }
1304
+ expect do
1305
+ @session.find(:css, 'span')
1306
+ end.to(raise_error(TestApp::TestAppOtherError) do |e|
1307
+ expect(e.cause).to be_a Capybara::CapybaraError
1308
+ expect(e.cause.message).to match(/Your application server raised an error/)
1309
+ end)
1310
+ end
414
1311
  end
415
1312
  end