capybara 2.18.0 → 3.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/History.md +945 -12
  4. data/License.txt +1 -1
  5. data/README.md +264 -90
  6. data/lib/capybara/config.rb +29 -57
  7. data/lib/capybara/cucumber.rb +2 -3
  8. data/lib/capybara/driver/base.rb +35 -18
  9. data/lib/capybara/driver/node.rb +40 -10
  10. data/lib/capybara/dsl.rb +10 -7
  11. data/lib/capybara/helpers.rb +70 -31
  12. data/lib/capybara/minitest/spec.rb +185 -83
  13. data/lib/capybara/minitest.rb +232 -112
  14. data/lib/capybara/node/actions.rb +274 -171
  15. data/lib/capybara/node/base.rb +42 -34
  16. data/lib/capybara/node/document.rb +15 -3
  17. data/lib/capybara/node/document_matchers.rb +19 -21
  18. data/lib/capybara/node/element.rb +362 -135
  19. data/lib/capybara/node/finders.rb +151 -137
  20. data/lib/capybara/node/matchers.rb +394 -209
  21. data/lib/capybara/node/simple.rb +59 -26
  22. data/lib/capybara/node/whitespace_normalizer.rb +81 -0
  23. data/lib/capybara/queries/active_element_query.rb +18 -0
  24. data/lib/capybara/queries/ancestor_query.rb +12 -9
  25. data/lib/capybara/queries/base_query.rb +39 -28
  26. data/lib/capybara/queries/current_path_query.rb +21 -27
  27. data/lib/capybara/queries/match_query.rb +14 -7
  28. data/lib/capybara/queries/selector_query.rb +659 -149
  29. data/lib/capybara/queries/sibling_query.rb +11 -9
  30. data/lib/capybara/queries/style_query.rb +45 -0
  31. data/lib/capybara/queries/text_query.rb +56 -38
  32. data/lib/capybara/queries/title_query.rb +8 -11
  33. data/lib/capybara/rack_test/browser.rb +114 -42
  34. data/lib/capybara/rack_test/css_handlers.rb +6 -4
  35. data/lib/capybara/rack_test/driver.rb +22 -17
  36. data/lib/capybara/rack_test/errors.rb +6 -0
  37. data/lib/capybara/rack_test/form.rb +93 -58
  38. data/lib/capybara/rack_test/node.rb +184 -81
  39. data/lib/capybara/rails.rb +3 -7
  40. data/lib/capybara/registration_container.rb +41 -0
  41. data/lib/capybara/registrations/drivers.rb +42 -0
  42. data/lib/capybara/registrations/patches/puma_ssl.rb +29 -0
  43. data/lib/capybara/registrations/servers.rb +66 -0
  44. data/lib/capybara/result.rb +97 -63
  45. data/lib/capybara/rspec/features.rb +17 -50
  46. data/lib/capybara/rspec/matcher_proxies.rb +52 -15
  47. data/lib/capybara/rspec/matchers/base.rb +113 -0
  48. data/lib/capybara/rspec/matchers/become_closed.rb +33 -0
  49. data/lib/capybara/rspec/matchers/compound.rb +88 -0
  50. data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
  51. data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
  52. data/lib/capybara/rspec/matchers/have_current_path.rb +29 -0
  53. data/lib/capybara/rspec/matchers/have_selector.rb +69 -0
  54. data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
  55. data/lib/capybara/rspec/matchers/have_text.rb +33 -0
  56. data/lib/capybara/rspec/matchers/have_title.rb +29 -0
  57. data/lib/capybara/rspec/matchers/match_selector.rb +27 -0
  58. data/lib/capybara/rspec/matchers/match_style.rb +43 -0
  59. data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
  60. data/lib/capybara/rspec/matchers.rb +146 -310
  61. data/lib/capybara/rspec.rb +7 -11
  62. data/lib/capybara/selector/builders/css_builder.rb +84 -0
  63. data/lib/capybara/selector/builders/xpath_builder.rb +71 -0
  64. data/lib/capybara/selector/css.rb +85 -13
  65. data/lib/capybara/selector/definition/button.rb +68 -0
  66. data/lib/capybara/selector/definition/checkbox.rb +26 -0
  67. data/lib/capybara/selector/definition/css.rb +10 -0
  68. data/lib/capybara/selector/definition/datalist_input.rb +35 -0
  69. data/lib/capybara/selector/definition/datalist_option.rb +25 -0
  70. data/lib/capybara/selector/definition/element.rb +28 -0
  71. data/lib/capybara/selector/definition/field.rb +40 -0
  72. data/lib/capybara/selector/definition/fieldset.rb +14 -0
  73. data/lib/capybara/selector/definition/file_field.rb +13 -0
  74. data/lib/capybara/selector/definition/fillable_field.rb +33 -0
  75. data/lib/capybara/selector/definition/frame.rb +17 -0
  76. data/lib/capybara/selector/definition/id.rb +6 -0
  77. data/lib/capybara/selector/definition/label.rb +62 -0
  78. data/lib/capybara/selector/definition/link.rb +55 -0
  79. data/lib/capybara/selector/definition/link_or_button.rb +16 -0
  80. data/lib/capybara/selector/definition/option.rb +27 -0
  81. data/lib/capybara/selector/definition/radio_button.rb +27 -0
  82. data/lib/capybara/selector/definition/select.rb +81 -0
  83. data/lib/capybara/selector/definition/table.rb +109 -0
  84. data/lib/capybara/selector/definition/table_row.rb +21 -0
  85. data/lib/capybara/selector/definition/xpath.rb +5 -0
  86. data/lib/capybara/selector/definition.rb +280 -0
  87. data/lib/capybara/selector/filter.rb +2 -17
  88. data/lib/capybara/selector/filter_set.rb +80 -33
  89. data/lib/capybara/selector/filters/base.rb +50 -6
  90. data/lib/capybara/selector/filters/expression_filter.rb +8 -26
  91. data/lib/capybara/selector/filters/locator_filter.rb +29 -0
  92. data/lib/capybara/selector/filters/node_filter.rb +16 -12
  93. data/lib/capybara/selector/regexp_disassembler.rb +211 -0
  94. data/lib/capybara/selector/selector.rb +93 -210
  95. data/lib/capybara/selector/xpath_extensions.rb +17 -0
  96. data/lib/capybara/selector.rb +475 -523
  97. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -0
  98. data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -0
  99. data/lib/capybara/selenium/atoms/src/getAttribute.js +161 -0
  100. data/lib/capybara/selenium/atoms/src/isDisplayed.js +454 -0
  101. data/lib/capybara/selenium/driver.rb +298 -267
  102. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +117 -0
  103. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +128 -0
  104. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +84 -0
  105. data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +26 -0
  106. data/lib/capybara/selenium/driver_specializations/safari_driver.rb +24 -0
  107. data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
  108. data/lib/capybara/selenium/extensions/find.rb +110 -0
  109. data/lib/capybara/selenium/extensions/html5_drag.rb +229 -0
  110. data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
  111. data/lib/capybara/selenium/extensions/scroll.rb +76 -0
  112. data/lib/capybara/selenium/node.rb +517 -145
  113. data/lib/capybara/selenium/nodes/chrome_node.rb +125 -0
  114. data/lib/capybara/selenium/nodes/edge_node.rb +110 -0
  115. data/lib/capybara/selenium/nodes/firefox_node.rb +136 -0
  116. data/lib/capybara/selenium/nodes/ie_node.rb +22 -0
  117. data/lib/capybara/selenium/nodes/safari_node.rb +118 -0
  118. data/lib/capybara/selenium/patches/atoms.rb +18 -0
  119. data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
  120. data/lib/capybara/selenium/patches/logs.rb +45 -0
  121. data/lib/capybara/selenium/patches/pause_duration_fix.rb +9 -0
  122. data/lib/capybara/selenium/patches/persistent_client.rb +20 -0
  123. data/lib/capybara/server/animation_disabler.rb +80 -0
  124. data/lib/capybara/server/checker.rb +44 -0
  125. data/lib/capybara/server/middleware.rb +71 -0
  126. data/lib/capybara/server.rb +59 -67
  127. data/lib/capybara/session/config.rb +81 -67
  128. data/lib/capybara/session/matchers.rb +28 -20
  129. data/lib/capybara/session.rb +337 -365
  130. data/lib/capybara/spec/public/jquery.js +5 -5
  131. data/lib/capybara/spec/public/offset.js +6 -0
  132. data/lib/capybara/spec/public/test.js +151 -12
  133. data/lib/capybara/spec/session/accept_alert_spec.rb +12 -11
  134. data/lib/capybara/spec/session/accept_confirm_spec.rb +6 -5
  135. data/lib/capybara/spec/session/accept_prompt_spec.rb +10 -10
  136. data/lib/capybara/spec/session/active_element_spec.rb +31 -0
  137. data/lib/capybara/spec/session/all_spec.rb +161 -57
  138. data/lib/capybara/spec/session/ancestor_spec.rb +27 -24
  139. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +68 -38
  140. data/lib/capybara/spec/session/assert_current_path_spec.rb +75 -0
  141. data/lib/capybara/spec/session/assert_selector_spec.rb +143 -0
  142. data/lib/capybara/spec/session/assert_style_spec.rb +26 -0
  143. data/lib/capybara/spec/session/assert_text_spec.rb +258 -0
  144. data/lib/capybara/spec/session/{assert_title.rb → assert_title_spec.rb} +22 -12
  145. data/lib/capybara/spec/session/attach_file_spec.rb +144 -69
  146. data/lib/capybara/spec/session/body_spec.rb +12 -13
  147. data/lib/capybara/spec/session/check_spec.rb +117 -55
  148. data/lib/capybara/spec/session/choose_spec.rb +64 -31
  149. data/lib/capybara/spec/session/click_button_spec.rb +231 -173
  150. data/lib/capybara/spec/session/click_link_or_button_spec.rb +55 -35
  151. data/lib/capybara/spec/session/click_link_spec.rb +93 -58
  152. data/lib/capybara/spec/session/current_scope_spec.rb +12 -11
  153. data/lib/capybara/spec/session/current_url_spec.rb +57 -39
  154. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +4 -4
  155. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +3 -2
  156. data/lib/capybara/spec/session/element/{assert_match_selector.rb → assert_match_selector_spec.rb} +11 -9
  157. data/lib/capybara/spec/session/element/match_css_spec.rb +18 -10
  158. data/lib/capybara/spec/session/element/match_xpath_spec.rb +9 -7
  159. data/lib/capybara/spec/session/element/matches_selector_spec.rb +71 -57
  160. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +8 -7
  161. data/lib/capybara/spec/session/evaluate_script_spec.rb +29 -8
  162. data/lib/capybara/spec/session/execute_script_spec.rb +10 -8
  163. data/lib/capybara/spec/session/fill_in_spec.rb +134 -43
  164. data/lib/capybara/spec/session/find_button_spec.rb +25 -24
  165. data/lib/capybara/spec/session/find_by_id_spec.rb +10 -9
  166. data/lib/capybara/spec/session/find_field_spec.rb +37 -41
  167. data/lib/capybara/spec/session/find_link_spec.rb +46 -17
  168. data/lib/capybara/spec/session/find_spec.rb +260 -145
  169. data/lib/capybara/spec/session/first_spec.rb +80 -52
  170. data/lib/capybara/spec/session/frame/frame_title_spec.rb +23 -0
  171. data/lib/capybara/spec/session/frame/frame_url_spec.rb +23 -0
  172. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +33 -20
  173. data/lib/capybara/spec/session/frame/within_frame_spec.rb +52 -32
  174. data/lib/capybara/spec/session/go_back_spec.rb +2 -1
  175. data/lib/capybara/spec/session/go_forward_spec.rb +2 -1
  176. data/lib/capybara/spec/session/has_all_selectors_spec.rb +31 -31
  177. data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
  178. data/lib/capybara/spec/session/has_any_selectors_spec.rb +29 -0
  179. data/lib/capybara/spec/session/has_button_spec.rb +100 -13
  180. data/lib/capybara/spec/session/has_css_spec.rb +272 -137
  181. data/lib/capybara/spec/session/has_current_path_spec.rb +60 -61
  182. data/lib/capybara/spec/session/has_element_spec.rb +47 -0
  183. data/lib/capybara/spec/session/has_field_spec.rb +139 -59
  184. data/lib/capybara/spec/session/has_link_spec.rb +47 -6
  185. data/lib/capybara/spec/session/has_none_selectors_spec.rb +42 -40
  186. data/lib/capybara/spec/session/has_select_spec.rb +107 -72
  187. data/lib/capybara/spec/session/has_selector_spec.rb +120 -71
  188. data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
  189. data/lib/capybara/spec/session/has_table_spec.rb +183 -5
  190. data/lib/capybara/spec/session/has_text_spec.rb +106 -62
  191. data/lib/capybara/spec/session/has_title_spec.rb +20 -14
  192. data/lib/capybara/spec/session/has_xpath_spec.rb +57 -38
  193. data/lib/capybara/spec/session/{headers.rb → headers_spec.rb} +3 -2
  194. data/lib/capybara/spec/session/html_spec.rb +14 -6
  195. data/lib/capybara/spec/session/matches_style_spec.rb +37 -0
  196. data/lib/capybara/spec/session/node_spec.rb +1024 -153
  197. data/lib/capybara/spec/session/node_wrapper_spec.rb +39 -0
  198. data/lib/capybara/spec/session/refresh_spec.rb +12 -6
  199. data/lib/capybara/spec/session/reset_session_spec.rb +82 -35
  200. data/lib/capybara/spec/session/{response_code.rb → response_code_spec.rb} +2 -1
  201. data/lib/capybara/spec/session/save_and_open_page_spec.rb +3 -2
  202. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +8 -12
  203. data/lib/capybara/spec/session/save_page_spec.rb +42 -55
  204. data/lib/capybara/spec/session/save_screenshot_spec.rb +16 -14
  205. data/lib/capybara/spec/session/screenshot_spec.rb +2 -2
  206. data/lib/capybara/spec/session/scroll_spec.rb +119 -0
  207. data/lib/capybara/spec/session/select_spec.rb +107 -81
  208. data/lib/capybara/spec/session/selectors_spec.rb +52 -19
  209. data/lib/capybara/spec/session/sibling_spec.rb +10 -10
  210. data/lib/capybara/spec/session/text_spec.rb +37 -21
  211. data/lib/capybara/spec/session/title_spec.rb +17 -5
  212. data/lib/capybara/spec/session/uncheck_spec.rb +43 -23
  213. data/lib/capybara/spec/session/unselect_spec.rb +39 -38
  214. data/lib/capybara/spec/session/visit_spec.rb +85 -53
  215. data/lib/capybara/spec/session/window/become_closed_spec.rb +24 -20
  216. data/lib/capybara/spec/session/window/current_window_spec.rb +5 -3
  217. data/lib/capybara/spec/session/window/open_new_window_spec.rb +5 -3
  218. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +27 -22
  219. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +12 -6
  220. data/lib/capybara/spec/session/window/window_spec.rb +97 -63
  221. data/lib/capybara/spec/session/window/windows_spec.rb +12 -10
  222. data/lib/capybara/spec/session/window/within_window_spec.rb +31 -86
  223. data/lib/capybara/spec/session/within_spec.rb +83 -44
  224. data/lib/capybara/spec/spec_helper.rb +54 -44
  225. data/lib/capybara/spec/test_app.rb +158 -43
  226. data/lib/capybara/spec/views/animated.erb +49 -0
  227. data/lib/capybara/spec/views/form.erb +163 -42
  228. data/lib/capybara/spec/views/frame_child.erb +4 -3
  229. data/lib/capybara/spec/views/frame_one.erb +2 -1
  230. data/lib/capybara/spec/views/frame_parent.erb +1 -1
  231. data/lib/capybara/spec/views/frame_two.erb +1 -1
  232. data/lib/capybara/spec/views/initial_alert.erb +2 -1
  233. data/lib/capybara/spec/views/layout.erb +10 -0
  234. data/lib/capybara/spec/views/obscured.erb +47 -0
  235. data/lib/capybara/spec/views/offset.erb +33 -0
  236. data/lib/capybara/spec/views/path.erb +2 -2
  237. data/lib/capybara/spec/views/popup_one.erb +1 -1
  238. data/lib/capybara/spec/views/popup_two.erb +1 -1
  239. data/lib/capybara/spec/views/react.erb +45 -0
  240. data/lib/capybara/spec/views/scroll.erb +21 -0
  241. data/lib/capybara/spec/views/spatial.erb +31 -0
  242. data/lib/capybara/spec/views/tables.erb +68 -1
  243. data/lib/capybara/spec/views/with_animation.erb +81 -0
  244. data/lib/capybara/spec/views/with_base_tag.erb +2 -2
  245. data/lib/capybara/spec/views/with_dragula.erb +24 -0
  246. data/lib/capybara/spec/views/with_fixed_header_footer.erb +2 -1
  247. data/lib/capybara/spec/views/with_hover.erb +3 -2
  248. data/lib/capybara/spec/views/with_hover1.erb +10 -0
  249. data/lib/capybara/spec/views/with_html.erb +69 -12
  250. data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
  251. data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
  252. data/lib/capybara/spec/views/with_js.erb +30 -5
  253. data/lib/capybara/spec/views/with_jstree.erb +26 -0
  254. data/lib/capybara/spec/views/with_namespace.erb +21 -0
  255. data/lib/capybara/spec/views/with_scope.erb +2 -2
  256. data/lib/capybara/spec/views/with_scope_other.erb +6 -0
  257. data/lib/capybara/spec/views/with_shadow.erb +31 -0
  258. data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
  259. data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
  260. data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
  261. data/lib/capybara/spec/views/with_windows.erb +1 -1
  262. data/lib/capybara/spec/views/within_frames.erb +5 -2
  263. data/lib/capybara/version.rb +2 -1
  264. data/lib/capybara/window.rb +36 -34
  265. data/lib/capybara.rb +134 -107
  266. data/spec/basic_node_spec.rb +60 -34
  267. data/spec/capybara_spec.rb +63 -88
  268. data/spec/counter_spec.rb +35 -0
  269. data/spec/css_builder_spec.rb +101 -0
  270. data/spec/css_splitter_spec.rb +38 -0
  271. data/spec/dsl_spec.rb +85 -64
  272. data/spec/filter_set_spec.rb +27 -9
  273. data/spec/fixtures/certificate.pem +25 -0
  274. data/spec/fixtures/key.pem +27 -0
  275. data/spec/fixtures/selenium_driver_rspec_failure.rb +6 -5
  276. data/spec/fixtures/selenium_driver_rspec_success.rb +6 -5
  277. data/spec/minitest_spec.rb +52 -7
  278. data/spec/minitest_spec_spec.rb +94 -63
  279. data/spec/per_session_config_spec.rb +14 -13
  280. data/spec/rack_test_spec.rb +194 -125
  281. data/spec/regexp_dissassembler_spec.rb +250 -0
  282. data/spec/result_spec.rb +111 -50
  283. data/spec/rspec/features_spec.rb +37 -31
  284. data/spec/rspec/scenarios_spec.rb +10 -8
  285. data/spec/rspec/shared_spec_matchers.rb +473 -422
  286. data/spec/rspec/views_spec.rb +5 -3
  287. data/spec/rspec_matchers_spec.rb +52 -11
  288. data/spec/rspec_spec.rb +109 -89
  289. data/spec/sauce_spec_chrome.rb +43 -0
  290. data/spec/selector_spec.rb +397 -68
  291. data/spec/selenium_spec_chrome.rb +187 -40
  292. data/spec/selenium_spec_chrome_remote.rb +96 -0
  293. data/spec/selenium_spec_edge.rb +60 -0
  294. data/spec/selenium_spec_firefox.rb +201 -41
  295. data/spec/selenium_spec_firefox_remote.rb +94 -0
  296. data/spec/selenium_spec_ie.rb +149 -0
  297. data/spec/selenium_spec_safari.rb +162 -0
  298. data/spec/server_spec.rb +213 -102
  299. data/spec/session_spec.rb +53 -16
  300. data/spec/shared_selenium_node.rb +79 -0
  301. data/spec/shared_selenium_session.rb +473 -122
  302. data/spec/spec_helper.rb +126 -7
  303. data/spec/whitespace_normalizer_spec.rb +54 -0
  304. data/spec/xpath_builder_spec.rb +93 -0
  305. metadata +355 -73
  306. data/.yard/templates_custom/default/class/html/selectors.erb +0 -38
  307. data/.yard/templates_custom/default/class/html/setup.rb +0 -17
  308. data/.yard/yard_extensions.rb +0 -78
  309. data/lib/capybara/query.rb +0 -7
  310. data/lib/capybara/rspec/compound.rb +0 -95
  311. data/lib/capybara/spec/session/assert_current_path.rb +0 -72
  312. data/lib/capybara/spec/session/assert_selector.rb +0 -148
  313. data/lib/capybara/spec/session/assert_text.rb +0 -234
  314. data/lib/capybara/spec/session/source_spec.rb +0 -0
  315. data/lib/capybara/spec/views/with_title.erb +0 -5
  316. data/spec/selenium_spec_marionette.rb +0 -127
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  Capybara::SpecHelper.spec '#find' do
3
4
  before do
4
5
  @session.visit('/with_html')
@@ -8,66 +9,67 @@ Capybara::SpecHelper.spec '#find' do
8
9
  Capybara::Selector.remove(:monkey)
9
10
  end
10
11
 
11
- it "should find the first element using the given locator" do
12
+ it 'should find the first element using the given locator' do
12
13
  expect(@session.find('//h1').text).to eq('This is a test')
13
14
  expect(@session.find("//input[@id='test_field']").value).to eq('monkey')
14
15
  end
15
16
 
16
- it "should find the first element using the given locator and options" do
17
+ it 'should find the first element using the given locator and options' do
17
18
  expect(@session.find('//a', text: 'Redirect')[:id]).to eq('red')
18
19
  expect(@session.find(:css, 'a', text: 'A link came first')[:title]).to eq('twas a fine link')
19
20
  end
20
21
 
21
- it "should raise an error if there are multiple matches" do
22
+ it 'should raise an error if there are multiple matches' do
22
23
  expect { @session.find('//a') }.to raise_error(Capybara::Ambiguous)
23
24
  end
24
25
 
25
- it "should wait for asynchronous load", requires: [:js] do
26
+ it 'should wait for asynchronous load', requires: [:js] do
27
+ Capybara.default_max_wait_time = 2
26
28
  @session.visit('/with_js')
27
29
  @session.click_link('Click me')
28
- expect(@session.find(:css, "a#has-been-clicked").text).to include('Has been clicked')
30
+ expect(@session.find(:css, 'a#has-been-clicked').text).to include('Has been clicked')
29
31
  end
30
32
 
31
- context "with :text option" do
33
+ context 'with :text option' do
32
34
  it "casts text's argument to string" do
33
- expect(@session.find(:css, '.number', text: 42)).to have_content("42")
35
+ expect(@session.find(:css, '.number', text: 42)).to have_content('42')
34
36
  end
35
37
  end
36
38
 
37
- context "with :wait option", requires: [:js] do
38
- it "should not wait for asynchronous load when `false` given" do
39
+ context 'with :wait option', requires: [:js] do
40
+ it 'should not wait for asynchronous load when `false` given' do
39
41
  @session.visit('/with_js')
40
42
  @session.click_link('Click me')
41
43
  expect do
42
- @session.find(:css, "a#has-been-clicked", wait: false)
44
+ @session.find(:css, 'a#has-been-clicked', wait: false)
43
45
  end.to raise_error(Capybara::ElementNotFound)
44
46
  end
45
47
 
46
- it "should not find element if it appears after given wait duration" do
48
+ it 'should not find element if it appears after given wait duration' do
47
49
  @session.visit('/with_js')
48
50
  @session.click_link('Slowly')
49
51
  expect do
50
- @session.find(:css, "a#slow-clicked", wait: 0.2)
52
+ @session.find(:css, 'a#slow-clicked', wait: 0.2)
51
53
  end.to raise_error(Capybara::ElementNotFound)
52
54
  end
53
55
 
54
- it "should find element if it appears before given wait duration" do
56
+ it 'should find element if it appears before given wait duration' do
55
57
  @session.visit('/with_js')
56
58
  @session.click_link('Click me')
57
- expect(@session.find(:css, "a#has-been-clicked", wait: 2.0).text).to include('Has been clicked')
59
+ expect(@session.find(:css, 'a#has-been-clicked', wait: 3.0).text).to include('Has been clicked')
58
60
  end
59
61
  end
60
62
 
61
- context "with frozen time", requires: [:js] do
63
+ context 'with frozen time', requires: [:js] do
62
64
  if defined?(Process::CLOCK_MONOTONIC)
63
- it "will time out even if time is frozen" do
65
+ it 'will time out even if time is frozen' do
64
66
  @session.visit('/with_js')
65
67
  now = Time.now
66
68
  allow(Time).to receive(:now).and_return(now)
67
69
  expect { @session.find('//isnotthere') }.to raise_error(Capybara::ElementNotFound)
68
70
  end
69
71
  else
70
- it "raises an error suggesting that Capybara is stuck in time" do
72
+ it 'raises an error suggesting that Capybara is stuck in time' do
71
73
  @session.visit('/with_js')
72
74
  now = Time.now
73
75
  allow(Time).to receive(:now).and_return(now)
@@ -76,26 +78,42 @@ Capybara::SpecHelper.spec '#find' do
76
78
  end
77
79
  end
78
80
 
79
- context "with css selectors" do
80
- it "should find the first element using the given locator" do
81
+ context 'with css selectors' do
82
+ it 'should find the first element using the given locator' do
81
83
  expect(@session.find(:css, 'h1').text).to eq('This is a test')
82
84
  expect(@session.find(:css, "input[id='test_field']").value).to eq('monkey')
83
85
  end
84
86
 
85
- it "should support pseudo selectors" do
87
+ it 'should support pseudo selectors' do
86
88
  expect(@session.find(:css, 'input:disabled').value).to eq('This is disabled')
87
89
  end
90
+
91
+ it 'should support escaping characters' do
92
+ expect(@session.find(:css, '#\31 escape\.me').text).to eq('needs escaping')
93
+ expect(@session.find(:css, '.\32 escape').text).to eq('needs escaping')
94
+ end
95
+
96
+ it 'should not warn about locator' do
97
+ expect { @session.find(:css, '#not_on_page') }.to raise_error Capybara::ElementNotFound do |e|
98
+ expect(e.message).not_to match(/you may be passing a CSS selector or XPath expression/)
99
+ end
100
+ end
88
101
  end
89
102
 
90
- context "with xpath selectors" do
91
- it "should find the first element using the given locator" do
103
+ context 'with xpath selectors' do
104
+ it 'should find the first element using the given locator' do
92
105
  expect(@session.find(:xpath, '//h1').text).to eq('This is a test')
93
106
  expect(@session.find(:xpath, "//input[@id='test_field']").value).to eq('monkey')
94
107
  end
108
+
109
+ it 'should warn if passed a non-valid locator type' do
110
+ expect { @session.find(:xpath, 123) }.to raise_error(Exception) # The exact error is not yet well defined
111
+ .and output(/must respond to to_xpath or be an instance of String/).to_stderr
112
+ end
95
113
  end
96
114
 
97
- context "with custom selector" do
98
- it "should use the custom selector" do
115
+ context 'with custom selector' do
116
+ it 'should use the custom selector' do
99
117
  Capybara.add_selector(:beatle) do
100
118
  xpath { |name| ".//*[@id='#{name}']" }
101
119
  end
@@ -104,8 +122,8 @@ Capybara::SpecHelper.spec '#find' do
104
122
  end
105
123
  end
106
124
 
107
- context "with custom selector with custom `match` block" do
108
- it "should use the custom selector when locator matches the block" do
125
+ context 'with custom selector with custom `match` block' do
126
+ it 'should use the custom selector when locator matches the block' do
109
127
  Capybara.add_selector(:beatle) do
110
128
  xpath { |num| ".//*[contains(@class, 'beatle')][#{num}]" }
111
129
  match { |value| value.is_a?(Integer) }
@@ -117,20 +135,21 @@ Capybara::SpecHelper.spec '#find' do
117
135
  end
118
136
  end
119
137
 
120
- context "with custom selector with custom filter" do
138
+ context 'with custom selector with custom filter' do
121
139
  before do
122
140
  Capybara.add_selector(:beatle) do
123
141
  xpath { |name| ".//li[contains(@class, 'beatle')][contains(text(), '#{name}')]" }
124
- filter(:type) { |node, type| node[:class].split(/\s+/).include?(type) }
142
+ node_filter(:type) { |node, type| node[:class].split(/\s+/).include?(type) }
143
+ node_filter(:fail) { |_node, _val| raise Capybara::ElementNotFound, 'fail' }
125
144
  end
126
145
  end
127
146
 
128
- it "should find elements that match the filter" do
147
+ it 'should find elements that match the filter' do
129
148
  expect(@session.find(:beatle, 'Paul', type: 'drummer').text).to eq('Paul')
130
149
  expect(@session.find(:beatle, 'Ringo', type: 'drummer').text).to eq('Ringo')
131
150
  end
132
151
 
133
- it "ignores filter when it is not given" do
152
+ it 'ignores filter when it is not given' do
134
153
  expect(@session.find(:beatle, 'Paul').text).to eq('Paul')
135
154
  expect(@session.find(:beatle, 'John').text).to eq('John')
136
155
  end
@@ -139,22 +158,26 @@ Capybara::SpecHelper.spec '#find' do
139
158
  expect { @session.find(:beatle, 'John', type: 'drummer') }.to raise_error(Capybara::ElementNotFound)
140
159
  expect { @session.find(:beatle, 'George', type: 'drummer') }.to raise_error(Capybara::ElementNotFound)
141
160
  end
161
+
162
+ it 'should not raise an ElementNotFound error from in a filter' do
163
+ expect { @session.find(:beatle, 'John', fail: 'something') }.to raise_error(Capybara::ElementNotFound, /beatle "John"/)
164
+ end
142
165
  end
143
166
 
144
- context "with custom selector with custom filter and default" do
167
+ context 'with custom selector with custom filter and default' do
145
168
  before do
146
169
  Capybara.add_selector(:beatle) do
147
170
  xpath { |name| ".//li[contains(@class, 'beatle')][contains(text(), '#{name}')]" }
148
- filter(:type, default: "drummer") { |node, type| node[:class].split(/\s+/).include?(type) }
171
+ node_filter(:type, default: 'drummer') { |node, type| node[:class].split(/\s+/).include?(type) }
149
172
  end
150
173
  end
151
174
 
152
- it "should find elements that match the filter" do
175
+ it 'should find elements that match the filter' do
153
176
  expect(@session.find(:beatle, 'Paul', type: 'drummer').text).to eq('Paul')
154
177
  expect(@session.find(:beatle, 'Ringo', type: 'drummer').text).to eq('Ringo')
155
178
  end
156
179
 
157
- it "should use default value when filter is not given" do
180
+ it 'should use default value when filter is not given' do
158
181
  expect(@session.find(:beatle, 'Paul').text).to eq('Paul')
159
182
  expect { @session.find(:beatle, 'John') }.to raise_error(Capybara::ElementNotFound)
160
183
  end
@@ -165,10 +188,10 @@ Capybara::SpecHelper.spec '#find' do
165
188
  end
166
189
  end
167
190
 
168
- context "with alternate filter set" do
191
+ context 'with alternate filter set' do
169
192
  before do
170
193
  Capybara::Selector::FilterSet.add(:value) do
171
- filter(:with) { |node, with| node.value == with.to_s }
194
+ node_filter(:with) { |node, with| node.value == with.to_s }
172
195
  end
173
196
 
174
197
  Capybara.add_selector(:id_with_field_filters) do
@@ -177,254 +200,346 @@ Capybara::SpecHelper.spec '#find' do
177
200
  end
178
201
  end
179
202
 
180
- it "should allow use of filters from custom filter set" do
203
+ it 'should allow use of filters from custom filter set' do
181
204
  expect(@session.find(:id, 'test_field', filter_set: :value, with: 'monkey').value).to eq('monkey')
182
- expect{ @session.find(:id, 'test_field', filter_set: :value, with: 'not_monkey') }.to raise_error(Capybara::ElementNotFound)
205
+ expect { @session.find(:id, 'test_field', filter_set: :value, with: 'not_monkey') }.to raise_error(Capybara::ElementNotFound)
183
206
  end
184
207
 
185
- it "should allow use of filter set from a different selector" do
208
+ it 'should allow use of filter set from a different selector' do
186
209
  expect(@session.find(:id, 'test_field', filter_set: :field, with: 'monkey').value).to eq('monkey')
187
- expect{ @session.find(:id, 'test_field', filter_set: :field, with: 'not_monkey') }.to raise_error(Capybara::ElementNotFound)
210
+ expect { @session.find(:id, 'test_field', filter_set: :field, with: 'not_monkey') }.to raise_error(Capybara::ElementNotFound)
188
211
  end
189
212
 
190
- it "should allow importing of filter set into selector" do
213
+ it 'should allow importing of filter set into selector' do
191
214
  expect(@session.find(:id_with_field_filters, 'test_field', with: 'monkey').value).to eq('monkey')
192
- expect{ @session.find(:id_with_field_filters, 'test_field', with: 'not_monkey') }.to raise_error(Capybara::ElementNotFound)
215
+ expect { @session.find(:id_with_field_filters, 'test_field', with: 'not_monkey') }.to raise_error(Capybara::ElementNotFound)
193
216
  end
194
217
  end
195
218
 
196
- context "with css as default selector" do
219
+ context 'with css as default selector' do
197
220
  before { Capybara.default_selector = :css }
198
- it "should find the first element using the given locator" do
221
+
222
+ after { Capybara.default_selector = :xpath }
223
+
224
+ it 'should find the first element using the given locator' do
199
225
  expect(@session.find('h1').text).to eq('This is a test')
200
226
  expect(@session.find("input[id='test_field']").value).to eq('monkey')
201
227
  end
202
- after { Capybara.default_selector = :xpath }
203
228
  end
204
229
 
205
- it "should raise ElementNotFound with a useful default message if nothing was found" do
230
+ it 'should raise ElementNotFound with a useful default message if nothing was found' do
206
231
  expect do
207
232
  @session.find(:xpath, '//div[@id="nosuchthing"]').to be_nil
208
- end.to raise_error(Capybara::ElementNotFound, "Unable to find visible xpath \"//div[@id=\\\"nosuchthing\\\"]\"")
233
+ end.to raise_error(Capybara::ElementNotFound, 'Unable to find xpath "//div[@id=\\"nosuchthing\\"]"')
234
+ end
235
+
236
+ context 'without locator' do
237
+ it 'should not output `nil` in the ElementNotFound message if nothing was found' do
238
+ expect do
239
+ @session.find(:label, text: 'no such thing').to be_nil
240
+ end.to raise_error(Capybara::ElementNotFound, 'Unable to find label')
241
+ end
209
242
  end
210
243
 
211
- it "should accept an XPath instance" do
244
+ it 'should accept an XPath instance' do
212
245
  @session.visit('/form')
213
- @xpath = Capybara::Selector.all[:fillable_field].call('First Name')
214
- expect(@xpath).to be_a(::XPath::Union)
246
+ @xpath = Capybara::Selector.new(:fillable_field, config: {}, format: :xpath).call('First Name')
247
+ expect(@xpath).to be_a(XPath::Union)
215
248
  expect(@session.find(@xpath).value).to eq('John')
216
249
  end
217
250
 
218
- context "with :exact option" do
219
- it "matches exactly when true" do
220
- expect(@session.find(:xpath, XPath.descendant(:input)[XPath.attr(:id).is("test_field")], exact: true).value).to eq("monkey")
251
+ context 'with :exact option' do
252
+ it 'matches exactly when true' do
253
+ expect(@session.find(:xpath, XPath.descendant(:input)[XPath.attr(:id).is('test_field')], exact: true).value).to eq('monkey')
221
254
  expect do
222
- @session.find(:xpath, XPath.descendant(:input)[XPath.attr(:id).is("est_fiel")], exact: true)
255
+ @session.find(:xpath, XPath.descendant(:input)[XPath.attr(:id).is('est_fiel')], exact: true)
223
256
  end.to raise_error(Capybara::ElementNotFound)
224
257
  end
225
258
 
226
- it "matches loosely when false" do
227
- expect(@session.find(:xpath, XPath.descendant(:input)[XPath.attr(:id).is("test_field")], exact: false).value).to eq("monkey")
228
- expect(@session.find(:xpath, XPath.descendant(:input)[XPath.attr(:id).is("est_fiel")], exact: false).value).to eq("monkey")
259
+ it 'matches loosely when false' do
260
+ expect(@session.find(:xpath, XPath.descendant(:input)[XPath.attr(:id).is('test_field')], exact: false).value).to eq('monkey')
261
+ expect(@session.find(:xpath, XPath.descendant(:input)[XPath.attr(:id).is('est_fiel')], exact: false).value).to eq('monkey')
229
262
  end
230
263
 
231
- it "defaults to `Capybara.exact`" do
264
+ it 'defaults to `Capybara.exact`' do
232
265
  Capybara.exact = true
233
266
  expect do
234
- @session.find(:xpath, XPath.descendant(:input)[XPath.attr(:id).is("est_fiel")])
267
+ @session.find(:xpath, XPath.descendant(:input)[XPath.attr(:id).is('est_fiel')])
235
268
  end.to raise_error(Capybara::ElementNotFound)
236
269
  Capybara.exact = false
237
- @session.find(:xpath, XPath.descendant(:input)[XPath.attr(:id).is("est_fiel")])
270
+ @session.find(:xpath, XPath.descendant(:input)[XPath.attr(:id).is('est_fiel')])
238
271
  end
239
272
 
240
- it "warns when the option has no effect" do
241
- expect_any_instance_of(Kernel).to receive(:warn).
242
- with('The :exact option only has an effect on queries using the XPath#is method. Using it with the query "#test_field" has no effect.')
243
- @session.find(:css, '#test_field', exact: true)
273
+ it 'warns when the option has no effect' do
274
+ expect { @session.find(:css, '#test_field', exact: true) }.to \
275
+ output(/^The :exact option only has an effect on queries using the XPath#is method. Using it with the query "#test_field" has no effect/).to_stderr
244
276
  end
245
277
  end
246
278
 
247
- context "with :match option" do
248
- context "when set to `one`" do
249
- it "raises an error when multiple matches exist" do
279
+ context 'with :match option' do
280
+ context 'when set to `one`' do
281
+ it 'raises an error when multiple matches exist' do
250
282
  expect do
251
- @session.find(:css, ".multiple", match: :one)
283
+ @session.find(:css, '.multiple', match: :one)
252
284
  end.to raise_error(Capybara::Ambiguous)
253
285
  end
254
- it "raises an error even if there the match is exact and the others are inexact" do
286
+
287
+ it 'raises an error even if there the match is exact and the others are inexact' do
255
288
  expect do
256
- @session.find(:xpath, XPath.descendant[XPath.attr(:class).is("almost_singular")], exact: false, match: :one)
289
+ @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular')], exact: false, match: :one)
257
290
  end.to raise_error(Capybara::Ambiguous)
258
291
  end
259
- it "returns the element if there is only one" do
260
- expect(@session.find(:css, ".singular", match: :one).text).to eq("singular")
292
+
293
+ it 'returns the element if there is only one' do
294
+ expect(@session.find(:css, '.singular', match: :one).text).to eq('singular')
261
295
  end
262
- it "raises an error if there is no match" do
296
+
297
+ it 'raises an error if there is no match' do
263
298
  expect do
264
- @session.find(:css, ".does-not-exist", match: :one)
299
+ @session.find(:css, '.does-not-exist', match: :one)
265
300
  end.to raise_error(Capybara::ElementNotFound)
266
301
  end
267
302
  end
268
303
 
269
- context "when set to `first`" do
270
- it "returns the first matched element" do
271
- expect(@session.find(:css, ".multiple", match: :first).text).to eq("multiple one")
304
+ context 'when set to `first`' do
305
+ it 'returns the first matched element' do
306
+ expect(@session.find(:css, '.multiple', match: :first).text).to eq('multiple one')
272
307
  end
273
- it "raises an error if there is no match" do
308
+
309
+ it 'raises an error if there is no match' do
274
310
  expect do
275
- @session.find(:css, ".does-not-exist", match: :first)
311
+ @session.find(:css, '.does-not-exist', match: :first)
276
312
  end.to raise_error(Capybara::ElementNotFound)
277
313
  end
278
314
  end
279
315
 
280
- context "when set to `smart`" do
281
- context "and `exact` set to `false`" do
282
- it "raises an error when there are multiple exact matches" do
316
+ context 'when set to `smart`' do
317
+ context 'and `exact` set to `false`' do
318
+ it 'raises an error when there are multiple exact matches' do
283
319
  expect do
284
- @session.find(:xpath, XPath.descendant[XPath.attr(:class).is("multiple")], match: :smart, exact: false)
320
+ @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('multiple')], match: :smart, exact: false)
285
321
  end.to raise_error(Capybara::Ambiguous)
286
322
  end
287
- it "finds a single exact match when there also are inexact matches" do
288
- result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is("almost_singular")], match: :smart, exact: false)
289
- expect(result.text).to eq("almost singular")
323
+
324
+ it 'finds a single exact match when there also are inexact matches' do
325
+ result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular')], match: :smart, exact: false)
326
+ expect(result.text).to eq('almost singular')
290
327
  end
291
- it "raises an error when there are multiple inexact matches" do
328
+
329
+ it 'raises an error when there are multiple inexact matches' do
292
330
  expect do
293
- @session.find(:xpath, XPath.descendant[XPath.attr(:class).is("almost_singul")], match: :smart, exact: false)
331
+ @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singul')], match: :smart, exact: false)
294
332
  end.to raise_error(Capybara::Ambiguous)
295
333
  end
296
- it "finds a single inexact match" do
297
- result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is("almost_singular but")], match: :smart, exact: false)
298
- expect(result.text).to eq("almost singular but not quite")
334
+
335
+ it 'finds a single inexact match' do
336
+ result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular but')], match: :smart, exact: false)
337
+ expect(result.text).to eq('almost singular but not quite')
299
338
  end
300
- it "raises an error if there is no match" do
339
+
340
+ it 'raises an error if there is no match' do
301
341
  expect do
302
- @session.find(:xpath, XPath.descendant[XPath.attr(:class).is("does-not-exist")], match: :smart, exact: false)
342
+ @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('does-not-exist')], match: :smart, exact: false)
303
343
  end.to raise_error(Capybara::ElementNotFound)
304
344
  end
305
345
  end
306
346
 
307
- context "with `exact` set to `true`" do
308
- it "raises an error when there are multiple exact matches" do
347
+ context 'with `exact` set to `true`' do
348
+ it 'raises an error when there are multiple exact matches' do
309
349
  expect do
310
- @session.find(:xpath, XPath.descendant[XPath.attr(:class).is("multiple")], match: :smart, exact: true)
350
+ @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('multiple')], match: :smart, exact: true)
311
351
  end.to raise_error(Capybara::Ambiguous)
312
352
  end
313
- it "finds a single exact match when there also are inexact matches" do
314
- result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is("almost_singular")], match: :smart, exact: true)
315
- expect(result.text).to eq("almost singular")
353
+
354
+ it 'finds a single exact match when there also are inexact matches' do
355
+ result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular')], match: :smart, exact: true)
356
+ expect(result.text).to eq('almost singular')
316
357
  end
317
- it "raises an error when there are multiple inexact matches" do
358
+
359
+ it 'raises an error when there are multiple inexact matches' do
318
360
  expect do
319
- @session.find(:xpath, XPath.descendant[XPath.attr(:class).is("almost_singul")], match: :smart, exact: true)
361
+ @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singul')], match: :smart, exact: true)
320
362
  end.to raise_error(Capybara::ElementNotFound)
321
363
  end
322
- it "raises an error when there is a single inexact matches" do
364
+
365
+ it 'raises an error when there is a single inexact matches' do
323
366
  expect do
324
- @session.find(:xpath, XPath.descendant[XPath.attr(:class).is("almost_singular but")], match: :smart, exact: true)
367
+ @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular but')], match: :smart, exact: true)
325
368
  end.to raise_error(Capybara::ElementNotFound)
326
369
  end
327
- it "raises an error if there is no match" do
370
+
371
+ it 'raises an error if there is no match' do
328
372
  expect do
329
- @session.find(:xpath, XPath.descendant[XPath.attr(:class).is("does-not-exist")], match: :smart, exact: true)
373
+ @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('does-not-exist')], match: :smart, exact: true)
330
374
  end.to raise_error(Capybara::ElementNotFound)
331
375
  end
332
376
  end
333
377
  end
334
378
 
335
- context "when set to `prefer_exact`" do
336
- context "and `exact` set to `false`" do
337
- it "picks the first one when there are multiple exact matches" do
338
- result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is("multiple")], match: :prefer_exact, exact: false)
339
- expect(result.text).to eq("multiple one")
379
+ context 'when set to `prefer_exact`' do
380
+ context 'and `exact` set to `false`' do
381
+ it 'picks the first one when there are multiple exact matches' do
382
+ result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('multiple')], match: :prefer_exact, exact: false)
383
+ expect(result.text).to eq('multiple one')
340
384
  end
341
- it "finds a single exact match when there also are inexact matches" do
342
- result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is("almost_singular")], match: :prefer_exact, exact: false)
343
- expect(result.text).to eq("almost singular")
385
+
386
+ it 'finds a single exact match when there also are inexact matches' do
387
+ result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular')], match: :prefer_exact, exact: false)
388
+ expect(result.text).to eq('almost singular')
344
389
  end
345
- it "picks the first one when there are multiple inexact matches" do
346
- result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is("almost_singul")], match: :prefer_exact, exact: false)
347
- expect(result.text).to eq("almost singular but not quite")
390
+
391
+ it 'picks the first one when there are multiple inexact matches' do
392
+ result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singul')], match: :prefer_exact, exact: false)
393
+ expect(result.text).to eq('almost singular but not quite')
348
394
  end
349
- it "finds a single inexact match" do
350
- result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is("almost_singular but")], match: :prefer_exact, exact: false)
351
- expect(result.text).to eq("almost singular but not quite")
395
+
396
+ it 'finds a single inexact match' do
397
+ result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular but')], match: :prefer_exact, exact: false)
398
+ expect(result.text).to eq('almost singular but not quite')
352
399
  end
353
- it "raises an error if there is no match" do
400
+
401
+ it 'raises an error if there is no match' do
354
402
  expect do
355
- @session.find(:xpath, XPath.descendant[XPath.attr(:class).is("does-not-exist")], match: :prefer_exact, exact: false)
403
+ @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('does-not-exist')], match: :prefer_exact, exact: false)
356
404
  end.to raise_error(Capybara::ElementNotFound)
357
405
  end
358
406
  end
359
407
 
360
- context "with `exact` set to `true`" do
361
- it "picks the first one when there are multiple exact matches" do
362
- result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is("multiple")], match: :prefer_exact, exact: true)
363
- expect(result.text).to eq("multiple one")
408
+ context 'with `exact` set to `true`' do
409
+ it 'picks the first one when there are multiple exact matches' do
410
+ result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('multiple')], match: :prefer_exact, exact: true)
411
+ expect(result.text).to eq('multiple one')
364
412
  end
365
- it "finds a single exact match when there also are inexact matches" do
366
- result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is("almost_singular")], match: :prefer_exact, exact: true)
367
- expect(result.text).to eq("almost singular")
413
+
414
+ it 'finds a single exact match when there also are inexact matches' do
415
+ result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular')], match: :prefer_exact, exact: true)
416
+ expect(result.text).to eq('almost singular')
368
417
  end
369
- it "raises an error if there are multiple inexact matches" do
418
+
419
+ it 'raises an error if there are multiple inexact matches' do
370
420
  expect do
371
- @session.find(:xpath, XPath.descendant[XPath.attr(:class).is("almost_singul")], match: :prefer_exact, exact: true)
421
+ @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singul')], match: :prefer_exact, exact: true)
372
422
  end.to raise_error(Capybara::ElementNotFound)
373
423
  end
374
- it "raises an error if there is a single inexact match" do
424
+
425
+ it 'raises an error if there is a single inexact match' do
375
426
  expect do
376
- @session.find(:xpath, XPath.descendant[XPath.attr(:class).is("almost_singular but")], match: :prefer_exact, exact: true)
427
+ @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular but')], match: :prefer_exact, exact: true)
377
428
  end.to raise_error(Capybara::ElementNotFound)
378
429
  end
379
- it "raises an error if there is no match" do
430
+
431
+ it 'raises an error if there is no match' do
380
432
  expect do
381
- @session.find(:xpath, XPath.descendant[XPath.attr(:class).is("does-not-exist")], match: :prefer_exact, exact: true)
433
+ @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('does-not-exist')], match: :prefer_exact, exact: true)
382
434
  end.to raise_error(Capybara::ElementNotFound)
383
435
  end
384
436
  end
385
437
  end
386
438
 
387
- it "defaults to `Capybara.match`" do
439
+ it 'defaults to `Capybara.match`' do
388
440
  Capybara.match = :one
389
441
  expect do
390
- @session.find(:css, ".multiple")
442
+ @session.find(:css, '.multiple')
391
443
  end.to raise_error(Capybara::Ambiguous)
392
444
  Capybara.match = :first
393
- expect(@session.find(:css, ".multiple").text).to eq("multiple one")
445
+ expect(@session.find(:css, '.multiple').text).to eq('multiple one')
394
446
  end
395
447
 
396
- it "raises an error when unknown option given" do
448
+ it 'raises an error when unknown option given' do
397
449
  expect do
398
- @session.find(:css, ".singular", match: :schmoo)
450
+ @session.find(:css, '.singular', match: :schmoo)
399
451
  end.to raise_error(ArgumentError)
400
452
  end
401
453
  end
402
454
 
403
- it "supports a custom filter block" do
404
- expect(@session.find(:css, 'input'){|node| node.disabled? }[:name]).to eq('disabled_text')
455
+ it 'supports a custom filter block' do
456
+ expect(@session.find(:css, 'input', &:disabled?)[:name]).to eq('disabled_text')
405
457
  end
406
458
 
407
- context "within a scope" do
459
+ context 'with spatial filters', requires: [:spatial] do
460
+ before do
461
+ @session.visit('/spatial')
462
+ end
463
+
464
+ let :center do
465
+ @session.find(:css, 'div.center')
466
+ end
467
+
468
+ it 'should find an element above another element' do
469
+ expect(@session.find(:css, 'div:not(.corner)', above: center).text).to eq('2')
470
+ end
471
+
472
+ it 'should find an element below another element' do
473
+ expect(@session.find(:css, 'div:not(.corner):not(.footer)', below: center).text).to eq('8')
474
+ end
475
+
476
+ it 'should find an element left of another element' do
477
+ expect(@session.find(:css, 'div:not(.corner)', left_of: center).text).to eq('4')
478
+ end
479
+
480
+ it 'should find an element right of another element' do
481
+ expect(@session.find(:css, 'div:not(.corner)', right_of: center).text).to eq('6')
482
+ end
483
+
484
+ it 'should combine spatial filters' do
485
+ expect(@session.find(:css, 'div', left_of: center, above: center).text).to eq('1')
486
+ expect(@session.find(:css, 'div', right_of: center, below: center).text).to eq('9')
487
+ end
488
+
489
+ it 'should find an element "near" another element' do
490
+ expect(@session.find(:css, 'div.distance', near: center).text).to eq('2')
491
+ end
492
+ end
493
+
494
+ context 'within a scope' do
408
495
  before do
409
496
  @session.visit('/with_scope')
410
497
  end
411
498
 
412
- it "should find the an element using the given locator" do
499
+ it 'should find the an element using the given locator' do
413
500
  @session.within(:xpath, "//div[@id='for_bar']") do
414
501
  expect(@session.find('.//li[1]').text).to match(/With Simple HTML/)
415
502
  end
416
503
  end
417
504
 
418
- it "should support pseudo selectors" do
505
+ it 'should support pseudo selectors' do
419
506
  @session.within(:xpath, "//div[@id='for_bar']") do
420
507
  expect(@session.find(:css, 'input:disabled').value).to eq('James')
421
508
  end
422
509
  end
423
510
  end
424
511
 
425
- it "should raise if selector type is unknown" do
512
+ it 'should raise if selector type is unknown' do
426
513
  expect do
427
514
  @session.find(:unknown, '//h1')
428
515
  end.to raise_error(ArgumentError)
429
516
  end
517
+
518
+ context 'with Capybara.test_id' do
519
+ it 'should not match on it when nil' do
520
+ Capybara.test_id = nil
521
+ expect(@session).not_to have_field('test_id')
522
+ end
523
+
524
+ it 'should work with the attribute set to `data-test-id` attribute' do
525
+ Capybara.test_id = 'data-test-id'
526
+ expect(@session.find(:field, 'test_id')[:id]).to eq 'test_field'
527
+ end
528
+
529
+ it 'should use a different attribute if set' do
530
+ Capybara.test_id = 'data-other-test-id'
531
+ expect(@session.find(:field, 'test_id')[:id]).to eq 'normal'
532
+ end
533
+
534
+ it 'should find a link with the test_id' do
535
+ Capybara.test_id = 'data-test-id'
536
+ expect(@session.find(:link, 'test-foo')[:id]).to eq 'foo'
537
+ end
538
+ end
539
+
540
+ it 'should warn if passed count options' do
541
+ allow(Capybara::Helpers).to receive(:warn)
542
+ @session.find('//h1', count: 44)
543
+ expect(Capybara::Helpers).to have_received(:warn).with(/'find' does not support count options/)
544
+ end
430
545
  end