capybara 2.7.0 → 3.35.3

Sign up to get free protection for your applications and to get access to all the features.
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