capybara 2.7.0 → 3.35.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (318) hide show
  1. checksums.yaml +5 -5
  2. data/.yardopts +1 -0
  3. data/History.md +1147 -11
  4. data/License.txt +1 -1
  5. data/README.md +252 -131
  6. data/lib/capybara/config.rb +92 -0
  7. data/lib/capybara/cucumber.rb +3 -3
  8. data/lib/capybara/driver/base.rb +52 -21
  9. data/lib/capybara/driver/node.rb +48 -14
  10. data/lib/capybara/dsl.rb +16 -9
  11. data/lib/capybara/helpers.rb +72 -81
  12. data/lib/capybara/minitest/spec.rb +267 -0
  13. data/lib/capybara/minitest.rb +385 -0
  14. data/lib/capybara/node/actions.rb +337 -89
  15. data/lib/capybara/node/base.rb +50 -32
  16. data/lib/capybara/node/document.rb +19 -3
  17. data/lib/capybara/node/document_matchers.rb +22 -24
  18. data/lib/capybara/node/element.rb +388 -125
  19. data/lib/capybara/node/finders.rb +231 -121
  20. data/lib/capybara/node/matchers.rb +503 -217
  21. data/lib/capybara/node/simple.rb +64 -27
  22. data/lib/capybara/queries/ancestor_query.rb +27 -0
  23. data/lib/capybara/queries/base_query.rb +87 -11
  24. data/lib/capybara/queries/current_path_query.rb +24 -24
  25. data/lib/capybara/queries/match_query.rb +15 -10
  26. data/lib/capybara/queries/selector_query.rb +675 -81
  27. data/lib/capybara/queries/sibling_query.rb +26 -0
  28. data/lib/capybara/queries/style_query.rb +45 -0
  29. data/lib/capybara/queries/text_query.rb +88 -20
  30. data/lib/capybara/queries/title_query.rb +9 -11
  31. data/lib/capybara/rack_test/browser.rb +63 -39
  32. data/lib/capybara/rack_test/css_handlers.rb +6 -4
  33. data/lib/capybara/rack_test/driver.rb +26 -16
  34. data/lib/capybara/rack_test/errors.rb +6 -0
  35. data/lib/capybara/rack_test/form.rb +73 -58
  36. data/lib/capybara/rack_test/node.rb +187 -67
  37. data/lib/capybara/rails.rb +4 -8
  38. data/lib/capybara/registration_container.rb +44 -0
  39. data/lib/capybara/registrations/drivers.rb +42 -0
  40. data/lib/capybara/registrations/patches/puma_ssl.rb +29 -0
  41. data/lib/capybara/registrations/servers.rb +45 -0
  42. data/lib/capybara/result.rb +142 -14
  43. data/lib/capybara/rspec/features.rb +17 -42
  44. data/lib/capybara/rspec/matcher_proxies.rb +82 -0
  45. data/lib/capybara/rspec/matchers/base.rb +111 -0
  46. data/lib/capybara/rspec/matchers/become_closed.rb +33 -0
  47. data/lib/capybara/rspec/matchers/compound.rb +88 -0
  48. data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
  49. data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
  50. data/lib/capybara/rspec/matchers/have_current_path.rb +29 -0
  51. data/lib/capybara/rspec/matchers/have_selector.rb +77 -0
  52. data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
  53. data/lib/capybara/rspec/matchers/have_text.rb +33 -0
  54. data/lib/capybara/rspec/matchers/have_title.rb +29 -0
  55. data/lib/capybara/rspec/matchers/match_selector.rb +27 -0
  56. data/lib/capybara/rspec/matchers/match_style.rb +43 -0
  57. data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
  58. data/lib/capybara/rspec/matchers.rb +143 -244
  59. data/lib/capybara/rspec.rb +10 -12
  60. data/lib/capybara/selector/builders/css_builder.rb +84 -0
  61. data/lib/capybara/selector/builders/xpath_builder.rb +71 -0
  62. data/lib/capybara/selector/css.rb +102 -0
  63. data/lib/capybara/selector/definition/button.rb +63 -0
  64. data/lib/capybara/selector/definition/checkbox.rb +26 -0
  65. data/lib/capybara/selector/definition/css.rb +10 -0
  66. data/lib/capybara/selector/definition/datalist_input.rb +35 -0
  67. data/lib/capybara/selector/definition/datalist_option.rb +25 -0
  68. data/lib/capybara/selector/definition/element.rb +28 -0
  69. data/lib/capybara/selector/definition/field.rb +40 -0
  70. data/lib/capybara/selector/definition/fieldset.rb +14 -0
  71. data/lib/capybara/selector/definition/file_field.rb +13 -0
  72. data/lib/capybara/selector/definition/fillable_field.rb +33 -0
  73. data/lib/capybara/selector/definition/frame.rb +17 -0
  74. data/lib/capybara/selector/definition/id.rb +6 -0
  75. data/lib/capybara/selector/definition/label.rb +62 -0
  76. data/lib/capybara/selector/definition/link.rb +54 -0
  77. data/lib/capybara/selector/definition/link_or_button.rb +16 -0
  78. data/lib/capybara/selector/definition/option.rb +27 -0
  79. data/lib/capybara/selector/definition/radio_button.rb +27 -0
  80. data/lib/capybara/selector/definition/select.rb +81 -0
  81. data/lib/capybara/selector/definition/table.rb +109 -0
  82. data/lib/capybara/selector/definition/table_row.rb +21 -0
  83. data/lib/capybara/selector/definition/xpath.rb +5 -0
  84. data/lib/capybara/selector/definition.rb +278 -0
  85. data/lib/capybara/selector/filter.rb +3 -46
  86. data/lib/capybara/selector/filter_set.rb +124 -0
  87. data/lib/capybara/selector/filters/base.rb +77 -0
  88. data/lib/capybara/selector/filters/expression_filter.rb +22 -0
  89. data/lib/capybara/selector/filters/locator_filter.rb +29 -0
  90. data/lib/capybara/selector/filters/node_filter.rb +31 -0
  91. data/lib/capybara/selector/regexp_disassembler.rb +214 -0
  92. data/lib/capybara/selector/selector.rb +155 -0
  93. data/lib/capybara/selector/xpath_extensions.rb +17 -0
  94. data/lib/capybara/selector.rb +232 -369
  95. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -0
  96. data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -0
  97. data/lib/capybara/selenium/atoms/src/getAttribute.js +161 -0
  98. data/lib/capybara/selenium/atoms/src/isDisplayed.js +454 -0
  99. data/lib/capybara/selenium/driver.rb +380 -142
  100. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +117 -0
  101. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +124 -0
  102. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +89 -0
  103. data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +26 -0
  104. data/lib/capybara/selenium/driver_specializations/safari_driver.rb +24 -0
  105. data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
  106. data/lib/capybara/selenium/extensions/find.rb +110 -0
  107. data/lib/capybara/selenium/extensions/html5_drag.rb +228 -0
  108. data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
  109. data/lib/capybara/selenium/extensions/scroll.rb +76 -0
  110. data/lib/capybara/selenium/logger_suppressor.rb +40 -0
  111. data/lib/capybara/selenium/node.rb +528 -97
  112. data/lib/capybara/selenium/nodes/chrome_node.rb +137 -0
  113. data/lib/capybara/selenium/nodes/edge_node.rb +104 -0
  114. data/lib/capybara/selenium/nodes/firefox_node.rb +136 -0
  115. data/lib/capybara/selenium/nodes/ie_node.rb +22 -0
  116. data/lib/capybara/selenium/nodes/safari_node.rb +118 -0
  117. data/lib/capybara/selenium/patches/action_pauser.rb +26 -0
  118. data/lib/capybara/selenium/patches/atoms.rb +18 -0
  119. data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
  120. data/lib/capybara/selenium/patches/logs.rb +45 -0
  121. data/lib/capybara/selenium/patches/pause_duration_fix.rb +9 -0
  122. data/lib/capybara/selenium/patches/persistent_client.rb +20 -0
  123. data/lib/capybara/server/animation_disabler.rb +63 -0
  124. data/lib/capybara/server/checker.rb +44 -0
  125. data/lib/capybara/server/middleware.rb +71 -0
  126. data/lib/capybara/server.rb +74 -71
  127. data/lib/capybara/session/config.rb +126 -0
  128. data/lib/capybara/session/matchers.rb +44 -27
  129. data/lib/capybara/session.rb +500 -297
  130. data/lib/capybara/spec/fixtures/no_extension +1 -0
  131. data/lib/capybara/spec/public/jquery.js +5 -5
  132. data/lib/capybara/spec/public/offset.js +6 -0
  133. data/lib/capybara/spec/public/test.js +168 -14
  134. data/lib/capybara/spec/session/accept_alert_spec.rb +37 -14
  135. data/lib/capybara/spec/session/accept_confirm_spec.rb +7 -6
  136. data/lib/capybara/spec/session/accept_prompt_spec.rb +38 -10
  137. data/lib/capybara/spec/session/all_spec.rb +179 -59
  138. data/lib/capybara/spec/session/ancestor_spec.rb +88 -0
  139. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +140 -0
  140. data/lib/capybara/spec/session/assert_current_path_spec.rb +75 -0
  141. data/lib/capybara/spec/session/assert_selector_spec.rb +143 -0
  142. data/lib/capybara/spec/session/assert_style_spec.rb +26 -0
  143. data/lib/capybara/spec/session/assert_text_spec.rb +258 -0
  144. data/lib/capybara/spec/session/assert_title_spec.rb +93 -0
  145. data/lib/capybara/spec/session/attach_file_spec.rb +154 -48
  146. data/lib/capybara/spec/session/body_spec.rb +12 -13
  147. data/lib/capybara/spec/session/check_spec.rb +168 -41
  148. data/lib/capybara/spec/session/choose_spec.rb +75 -23
  149. data/lib/capybara/spec/session/click_button_spec.rb +243 -175
  150. data/lib/capybara/spec/session/click_link_or_button_spec.rb +57 -32
  151. data/lib/capybara/spec/session/click_link_spec.rb +100 -53
  152. data/lib/capybara/spec/session/current_scope_spec.rb +11 -10
  153. data/lib/capybara/spec/session/current_url_spec.rb +61 -35
  154. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +7 -7
  155. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +5 -4
  156. data/lib/capybara/spec/session/element/{assert_match_selector.rb → assert_match_selector_spec.rb} +13 -6
  157. data/lib/capybara/spec/session/element/match_css_spec.rb +21 -7
  158. data/lib/capybara/spec/session/element/match_xpath_spec.rb +9 -7
  159. data/lib/capybara/spec/session/element/matches_selector_spec.rb +91 -34
  160. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +23 -0
  161. data/lib/capybara/spec/session/evaluate_script_spec.rb +45 -3
  162. data/lib/capybara/spec/session/execute_script_spec.rb +24 -4
  163. data/lib/capybara/spec/session/fill_in_spec.rb +166 -64
  164. data/lib/capybara/spec/session/find_button_spec.rb +37 -18
  165. data/lib/capybara/spec/session/find_by_id_spec.rb +10 -9
  166. data/lib/capybara/spec/session/find_field_spec.rb +57 -34
  167. data/lib/capybara/spec/session/find_link_spec.rb +47 -10
  168. data/lib/capybara/spec/session/find_spec.rb +290 -144
  169. data/lib/capybara/spec/session/first_spec.rb +91 -48
  170. data/lib/capybara/spec/session/frame/frame_title_spec.rb +23 -0
  171. data/lib/capybara/spec/session/frame/frame_url_spec.rb +23 -0
  172. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +116 -0
  173. data/lib/capybara/spec/session/frame/within_frame_spec.rb +112 -0
  174. data/lib/capybara/spec/session/go_back_spec.rb +3 -2
  175. data/lib/capybara/spec/session/go_forward_spec.rb +3 -2
  176. data/lib/capybara/spec/session/has_all_selectors_spec.rb +69 -0
  177. data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
  178. data/lib/capybara/spec/session/has_any_selectors_spec.rb +25 -0
  179. data/lib/capybara/spec/session/has_button_spec.rb +76 -19
  180. data/lib/capybara/spec/session/has_css_spec.rb +277 -131
  181. data/lib/capybara/spec/session/has_current_path_spec.rb +98 -26
  182. data/lib/capybara/spec/session/has_field_spec.rb +177 -107
  183. data/lib/capybara/spec/session/has_link_spec.rb +13 -12
  184. data/lib/capybara/spec/session/has_none_selectors_spec.rb +78 -0
  185. data/lib/capybara/spec/session/has_select_spec.rb +191 -95
  186. data/lib/capybara/spec/session/has_selector_spec.rb +128 -64
  187. data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
  188. data/lib/capybara/spec/session/has_table_spec.rb +172 -5
  189. data/lib/capybara/spec/session/has_text_spec.rb +126 -60
  190. data/lib/capybara/spec/session/has_title_spec.rb +35 -12
  191. data/lib/capybara/spec/session/has_xpath_spec.rb +74 -53
  192. data/lib/capybara/spec/session/{headers.rb → headers_spec.rb} +3 -2
  193. data/lib/capybara/spec/session/html_spec.rb +14 -6
  194. data/lib/capybara/spec/session/matches_style_spec.rb +35 -0
  195. data/lib/capybara/spec/session/node_spec.rb +1028 -131
  196. data/lib/capybara/spec/session/node_wrapper_spec.rb +39 -0
  197. data/lib/capybara/spec/session/refresh_spec.rb +34 -0
  198. data/lib/capybara/spec/session/reset_session_spec.rb +75 -34
  199. data/lib/capybara/spec/session/{response_code.rb → response_code_spec.rb} +2 -1
  200. data/lib/capybara/spec/session/save_and_open_page_spec.rb +3 -2
  201. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +11 -15
  202. data/lib/capybara/spec/session/save_page_spec.rb +42 -55
  203. data/lib/capybara/spec/session/save_screenshot_spec.rb +16 -14
  204. data/lib/capybara/spec/session/screenshot_spec.rb +2 -2
  205. data/lib/capybara/spec/session/scroll_spec.rb +117 -0
  206. data/lib/capybara/spec/session/select_spec.rb +112 -85
  207. data/lib/capybara/spec/session/selectors_spec.rb +71 -8
  208. data/lib/capybara/spec/session/sibling_spec.rb +52 -0
  209. data/lib/capybara/spec/session/text_spec.rb +38 -23
  210. data/lib/capybara/spec/session/title_spec.rb +17 -5
  211. data/lib/capybara/spec/session/uncheck_spec.rb +71 -12
  212. data/lib/capybara/spec/session/unselect_spec.rb +44 -43
  213. data/lib/capybara/spec/session/visit_spec.rb +99 -32
  214. data/lib/capybara/spec/session/window/become_closed_spec.rb +33 -29
  215. data/lib/capybara/spec/session/window/current_window_spec.rb +5 -3
  216. data/lib/capybara/spec/session/window/open_new_window_spec.rb +5 -3
  217. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +39 -30
  218. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +17 -10
  219. data/lib/capybara/spec/session/window/window_spec.rb +121 -73
  220. data/lib/capybara/spec/session/window/windows_spec.rb +12 -10
  221. data/lib/capybara/spec/session/window/within_window_spec.rb +52 -82
  222. data/lib/capybara/spec/session/within_spec.rb +76 -43
  223. data/lib/capybara/spec/spec_helper.rb +67 -33
  224. data/lib/capybara/spec/test_app.rb +85 -36
  225. data/lib/capybara/spec/views/animated.erb +49 -0
  226. data/lib/capybara/spec/views/buttons.erb +1 -1
  227. data/lib/capybara/spec/views/fieldsets.erb +1 -1
  228. data/lib/capybara/spec/views/form.erb +227 -20
  229. data/lib/capybara/spec/views/frame_child.erb +10 -2
  230. data/lib/capybara/spec/views/frame_one.erb +2 -1
  231. data/lib/capybara/spec/views/frame_parent.erb +2 -2
  232. data/lib/capybara/spec/views/frame_two.erb +1 -1
  233. data/lib/capybara/spec/views/header_links.erb +1 -1
  234. data/lib/capybara/spec/views/host_links.erb +1 -1
  235. data/lib/capybara/spec/views/initial_alert.erb +10 -0
  236. data/lib/capybara/spec/views/obscured.erb +47 -0
  237. data/lib/capybara/spec/views/offset.erb +32 -0
  238. data/lib/capybara/spec/views/path.erb +1 -1
  239. data/lib/capybara/spec/views/popup_one.erb +1 -1
  240. data/lib/capybara/spec/views/popup_two.erb +1 -1
  241. data/lib/capybara/spec/views/postback.erb +1 -1
  242. data/lib/capybara/spec/views/react.erb +45 -0
  243. data/lib/capybara/spec/views/scroll.erb +20 -0
  244. data/lib/capybara/spec/views/spatial.erb +31 -0
  245. data/lib/capybara/spec/views/tables.erb +69 -2
  246. data/lib/capybara/spec/views/with_animation.erb +82 -0
  247. data/lib/capybara/spec/views/with_base_tag.erb +1 -1
  248. data/lib/capybara/spec/views/with_count.erb +1 -1
  249. data/lib/capybara/spec/views/with_dragula.erb +24 -0
  250. data/lib/capybara/spec/views/with_fixed_header_footer.erb +17 -0
  251. data/lib/capybara/spec/views/with_hover.erb +7 -1
  252. data/lib/capybara/spec/views/with_hover1.erb +10 -0
  253. data/lib/capybara/spec/views/with_html.erb +100 -10
  254. data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
  255. data/lib/capybara/spec/views/with_html_entities.erb +1 -1
  256. data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
  257. data/lib/capybara/spec/views/with_js.erb +49 -3
  258. data/lib/capybara/spec/views/with_jstree.erb +26 -0
  259. data/lib/capybara/spec/views/with_namespace.erb +20 -0
  260. data/lib/capybara/spec/views/with_scope.erb +1 -1
  261. data/lib/capybara/spec/views/with_scope_other.erb +6 -0
  262. data/lib/capybara/spec/views/with_simple_html.erb +1 -1
  263. data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
  264. data/lib/capybara/spec/views/with_title.erb +1 -1
  265. data/lib/capybara/spec/views/with_unload_alert.erb +3 -1
  266. data/lib/capybara/spec/views/with_windows.erb +7 -1
  267. data/lib/capybara/spec/views/within_frames.erb +6 -3
  268. data/lib/capybara/version.rb +2 -1
  269. data/lib/capybara/window.rb +39 -21
  270. data/lib/capybara.rb +208 -186
  271. data/spec/basic_node_spec.rb +52 -39
  272. data/spec/capybara_spec.rb +72 -50
  273. data/spec/css_builder_spec.rb +101 -0
  274. data/spec/css_splitter_spec.rb +38 -0
  275. data/spec/dsl_spec.rb +81 -61
  276. data/spec/filter_set_spec.rb +46 -0
  277. data/spec/fixtures/capybara.csv +1 -0
  278. data/spec/fixtures/certificate.pem +25 -0
  279. data/spec/fixtures/key.pem +27 -0
  280. data/spec/fixtures/selenium_driver_rspec_failure.rb +7 -3
  281. data/spec/fixtures/selenium_driver_rspec_success.rb +7 -3
  282. data/spec/minitest_spec.rb +164 -0
  283. data/spec/minitest_spec_spec.rb +162 -0
  284. data/spec/per_session_config_spec.rb +68 -0
  285. data/spec/rack_test_spec.rb +189 -96
  286. data/spec/regexp_dissassembler_spec.rb +250 -0
  287. data/spec/result_spec.rb +143 -13
  288. data/spec/rspec/features_spec.rb +38 -32
  289. data/spec/rspec/scenarios_spec.rb +9 -7
  290. data/spec/rspec/shared_spec_matchers.rb +959 -0
  291. data/spec/rspec/views_spec.rb +9 -3
  292. data/spec/rspec_matchers_spec.rb +62 -0
  293. data/spec/rspec_spec.rb +127 -30
  294. data/spec/sauce_spec_chrome.rb +43 -0
  295. data/spec/selector_spec.rb +458 -37
  296. data/spec/selenium_spec_chrome.rb +196 -9
  297. data/spec/selenium_spec_chrome_remote.rb +100 -0
  298. data/spec/selenium_spec_edge.rb +47 -0
  299. data/spec/selenium_spec_firefox.rb +210 -0
  300. data/spec/selenium_spec_firefox_remote.rb +80 -0
  301. data/spec/selenium_spec_ie.rb +150 -0
  302. data/spec/selenium_spec_safari.rb +148 -0
  303. data/spec/server_spec.rb +200 -101
  304. data/spec/session_spec.rb +91 -0
  305. data/spec/shared_selenium_node.rb +83 -0
  306. data/spec/shared_selenium_session.rb +558 -0
  307. data/spec/spec_helper.rb +94 -2
  308. data/spec/xpath_builder_spec.rb +93 -0
  309. metadata +420 -60
  310. data/lib/capybara/query.rb +0 -7
  311. data/lib/capybara/spec/session/assert_current_path.rb +0 -60
  312. data/lib/capybara/spec/session/assert_selector.rb +0 -148
  313. data/lib/capybara/spec/session/assert_text.rb +0 -196
  314. data/lib/capybara/spec/session/assert_title.rb +0 -70
  315. data/lib/capybara/spec/session/source_spec.rb +0 -0
  316. data/lib/capybara/spec/session/within_frame_spec.rb +0 -53
  317. data/spec/rspec/matchers_spec.rb +0 -827
  318. data/spec/selenium_spec.rb +0 -151
@@ -1,8 +1,14 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'spec_helper'
3
4
 
4
- RSpec.describe "capybara/rspec", type: :view do
5
- it "allows matchers to be used on strings" do
6
- expect(%{<h1>Test header</h1>}).to have_css("h1", text: "Test header")
5
+ RSpec.describe 'capybara/rspec', type: :view do
6
+ it 'allows matchers to be used on strings' do
7
+ html = %(<h1>Test header</h1>)
8
+ expect(html).to have_css('h1', text: 'Test header')
9
+ end
10
+
11
+ it "doesn't include RSpecMatcherProxies" do
12
+ expect(self.class.ancestors).not_to include(Capybara::RSpecMatcherProxies)
7
13
  end
8
14
  end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe 'Capybara RSpec Matchers', type: :feature do
6
+ context 'after called on session' do
7
+ it 'HaveSelector should allow getting a description of the matcher' do
8
+ visit('/with_html')
9
+ matcher = have_selector(:css, 'h2.head', minimum: 3)
10
+ expect(page).to matcher
11
+ expect { matcher.description }.not_to raise_error
12
+ end
13
+
14
+ it 'HaveText should allow getting a description' do
15
+ visit('/with_html')
16
+ matcher = have_text('Lorem')
17
+ expect(page).to matcher
18
+ expect { matcher.description }.not_to raise_error
19
+ end
20
+
21
+ it 'should produce the same error for .to have_no_xxx and .not_to have_xxx' do
22
+ visit('/with_html')
23
+ not_to_msg = error_msg_for { expect(page).not_to have_selector(:css, '#referrer') }
24
+ have_no_msg = error_msg_for { expect(page).to have_no_selector(:css, '#referrer') }
25
+ expect(not_to_msg).to eq have_no_msg
26
+
27
+ not_to_msg = error_msg_for { expect(page).not_to have_text('This is a test') }
28
+ have_no_msg = error_msg_for { expect(page).to have_no_text('This is a test') }
29
+ expect(not_to_msg).to eq have_no_msg
30
+ end
31
+ end
32
+
33
+ context 'after called on element' do
34
+ it 'HaveSelector should allow getting a description' do
35
+ visit('/with_html')
36
+ el = find(:css, '#first')
37
+ matcher = have_selector(:css, 'a#foo')
38
+ expect(el).to matcher
39
+ expect { matcher.description }.not_to raise_error
40
+ end
41
+
42
+ it 'MatchSelector should allow getting a description' do
43
+ visit('/with_html')
44
+ el = find(:css, '#first')
45
+ matcher = match_selector(:css, '#first')
46
+ expect(el).to matcher
47
+ expect { matcher.description }.not_to raise_error
48
+ end
49
+
50
+ it 'HaveText should allow getting a description' do
51
+ visit('/with_html')
52
+ el = find(:css, '#first')
53
+ matcher = have_text('Lorem')
54
+ expect(el).to matcher
55
+ expect { matcher.description }.not_to raise_error
56
+ end
57
+ end
58
+
59
+ def error_msg_for(&block)
60
+ expect(&block).to(raise_error { |e| return e.message })
61
+ end
62
+ end
data/spec/rspec_spec.rb CHANGED
@@ -1,52 +1,149 @@
1
1
  # frozen_string_literal: true
2
+
3
+ # rubocop:disable RSpec/MultipleDescribes
4
+
2
5
  require 'spec_helper'
3
6
 
4
- RSpec.describe 'capybara/rspec', :type => :feature do
5
- it "should include Capybara in rspec" do
6
- visit('/foo')
7
- expect(page.body).to include('Another World')
8
- end
7
+ RSpec.describe 'capybara/rspec' do
8
+ context 'Feature', type: :feature do
9
+ it 'should include Capybara in rspec' do
10
+ visit('/foo')
11
+ expect(page.body).to include('Another World')
12
+ end
13
+
14
+ it 'should include RSpec matcher proxies' do
15
+ expect(self.class.ancestors).to include Capybara::RSpecMatcherProxies
16
+ end
17
+
18
+ context 'resetting session' do
19
+ it 'sets a cookie in one example...' do
20
+ visit('/set_cookie')
21
+ expect(page.body).to include('Cookie set to test_cookie')
22
+ end
9
23
 
10
- context "resetting session" do
11
- it "sets a cookie in one example..." do
12
- visit('/set_cookie')
13
- expect(page.body).to include('Cookie set to test_cookie')
24
+ it '...then it is not available in the next' do
25
+ visit('/get_cookie')
26
+ expect(page.body).not_to include('test_cookie')
27
+ end
14
28
  end
15
29
 
16
- it "...then it is not available in the next" do
17
- visit('/get_cookie')
18
- expect(page.body).not_to include('test_cookie')
30
+ context 'setting the current driver' do
31
+ it 'sets the current driver in one example...' do
32
+ Capybara.current_driver = :selenium
33
+ end
34
+
35
+ it '...then it has returned to the default in the next example' do
36
+ expect(Capybara.current_driver).to eq(:rack_test)
37
+ end
19
38
  end
20
- end
21
39
 
22
- context "setting the current driver" do
23
- it "sets the current driver in one example..." do
24
- Capybara.current_driver = :selenium
40
+ it 'switches to the javascript driver when giving it as metadata', js: true do
41
+ expect(Capybara.current_driver).to eq(Capybara.javascript_driver)
25
42
  end
26
43
 
27
- it "...then it has returned to the default in the next example" do
28
- expect(Capybara.current_driver).to eq(:rack_test)
44
+ it 'switches to the given driver when giving it as metadata', driver: :culerity do
45
+ expect(Capybara.current_driver).to eq(:culerity)
29
46
  end
30
- end
31
47
 
32
- it "switches to the javascript driver when giving it as metadata", :js => true do
33
- expect(Capybara.current_driver).to eq(Capybara.javascript_driver)
34
- end
48
+ describe '#all' do
49
+ it 'allows access to the Capybara finder' do
50
+ visit('/with_html')
51
+ found = all(:css, 'h2') { |element| element[:class] == 'head' }
52
+ expect(found.size).to eq(5)
53
+ end
54
+
55
+ it 'allows access to the RSpec matcher' do
56
+ visit('/with_html')
57
+ strings = %w[test1 test2]
58
+ expect(strings).to all(be_a(String))
59
+ end
60
+ end
61
+
62
+ describe '#within' do
63
+ it 'allows access to the Capybara scoper' do
64
+ visit('/with_html')
65
+ expect do
66
+ within(:css, '#does_not_exist') { click_link 'Go to simple' }
67
+ end.to raise_error(Capybara::ElementNotFound)
68
+ end
35
69
 
36
- it "switches to the given driver when giving it as metadata", :driver => :culerity do
37
- expect(Capybara.current_driver).to eq(:culerity)
70
+ it 'allows access to the RSpec matcher' do
71
+ visit('/with_html')
72
+ # This reads terribly, but must call #within
73
+ expect(find(:css, 'span.number').text.to_i).to within(1).of(41)
74
+ end
75
+ end
38
76
  end
39
- end
40
77
 
41
- RSpec.describe 'capybara/rspec', :type => :other do
42
- it "should not include Capybara" do
43
- expect { visit('/') }.to raise_error(NoMethodError)
78
+ context 'Type: Other', type: :other do
79
+ context 'when RSpec::Matchers is included after Capybara::DSL' do
80
+ let(:test_class_instance) do
81
+ Class.new do
82
+ include Capybara::DSL
83
+ include RSpec::Matchers
84
+ end.new
85
+ end
86
+
87
+ describe '#all' do
88
+ it 'allows access to the Capybara finder' do
89
+ test_class_instance.visit('/with_html')
90
+ expect(test_class_instance.all(:css, 'h2.head').size).to eq(5)
91
+ end
92
+
93
+ it 'allows access to the RSpec matcher' do
94
+ test_class_instance.visit('/with_html')
95
+ strings = %w[test1 test2]
96
+ expect(strings).to test_class_instance.all(be_a(String))
97
+ end
98
+ end
99
+
100
+ describe '#within' do
101
+ it 'allows access to the Capybara scoper' do
102
+ test_class_instance.visit('/with_html')
103
+ expect do
104
+ test_class_instance.within(:css, '#does_not_exist') { test_class_instance.click_link 'Go to simple' }
105
+ end.to raise_error(Capybara::ElementNotFound)
106
+ end
107
+
108
+ it 'allows access to the RSpec matcher' do
109
+ test_class_instance.visit('/with_html')
110
+ # This reads terribly, but must call #within
111
+ expect(test_class_instance.find(:css, 'span.number').text.to_i).to test_class_instance.within(1).of(41)
112
+ end
113
+ end
114
+
115
+ context 'when `match_when_negated` is not defined in a matcher' do
116
+ before do
117
+ RSpec::Matchers.define :only_match_matcher do |expected|
118
+ match do |actual|
119
+ !(actual ^ expected)
120
+ end
121
+ end
122
+ end
123
+
124
+ it 'can be called with `not_to`' do
125
+ # This test is for a bug in jruby where `super` isn't defined correctly - https://github.com/jruby/jruby/issues/4678
126
+ # Reported in https://github.com/teamcapybara/capybara/issues/2115
127
+ test_class_instance.instance_eval do
128
+ expect do
129
+ expect(true).not_to only_match_matcher(false) # rubocop:disable RSpec/ExpectActual
130
+ end.not_to raise_error
131
+ end
132
+ end
133
+ end
134
+ end
135
+
136
+ it 'should not include Capybara' do
137
+ expect { visit('/') }.to raise_error(NoMethodError)
138
+ end
44
139
  end
45
140
  end
46
141
 
47
- feature "Feature DSL" do
48
- scenario "is pulled in" do
142
+ feature 'Feature DSL' do
143
+ scenario 'is pulled in' do
49
144
  visit('/foo')
50
145
  expect(page.body).to include('Another World')
51
146
  end
52
147
  end
148
+
149
+ # rubocop:enable RSpec/MultipleDescribes
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'selenium-webdriver'
5
+
6
+ require 'sauce_whisk'
7
+ # require 'shared_selenium_session'
8
+ # require 'shared_selenium_node'
9
+ # require 'rspec/shared_spec_matchers'
10
+
11
+ Capybara.register_driver :sauce_chrome do |app|
12
+ options = {
13
+ selenium_version: '3.141.59',
14
+ platform: 'macOS 10.12',
15
+ browser_name: 'chrome',
16
+ version: '65.0',
17
+ name: 'Capybara test',
18
+ build: ENV['TRAVIS_REPO_SLUG'] || "Ruby-RSpec-Selenium: Local-#{Time.now.to_i}",
19
+ username: ENV['SAUCE_USERNAME'],
20
+ access_key: ENV['SAUCE_ACCESS_KEY']
21
+ }
22
+
23
+ options.delete(:browser_name)
24
+
25
+ capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(options)
26
+ url = 'https://ondemand.saucelabs.com:443/wd/hub'
27
+
28
+ Capybara::Selenium::Driver.new(app,
29
+ browser: :remote, url: url,
30
+ desired_capabilities: capabilities,
31
+ options: Selenium::WebDriver::Chrome::Options.new(args: ['']))
32
+ end
33
+
34
+ CHROME_REMOTE_DRIVER = :sauce_chrome
35
+
36
+ module TestSessions
37
+ Chrome = Capybara::Session.new(CHROME_REMOTE_DRIVER, TestApp)
38
+ end
39
+
40
+ skipped_tests = %i[response_headers status_code trigger download]
41
+
42
+ Capybara::SpecHelper.run_specs TestSessions::Chrome, CHROME_REMOTE_DRIVER.to_s, capybara_skip: skipped_tests do |example|
43
+ end
@@ -1,107 +1,528 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'spec_helper'
3
4
 
4
5
  RSpec.describe Capybara do
6
+ include Capybara::RSpecMatchers
5
7
  describe 'Selectors' do
6
8
  let :string do
7
- Capybara.string <<-STRING
9
+ described_class.string <<-STRING
8
10
  <html>
9
11
  <head>
10
12
  <title>selectors</title>
11
13
  </head>
12
14
  <body>
13
- <div class="a" id="page">
14
- <div class="b" id="content">
15
- <h1 class="a">Totally awesome</h1>
15
+ <div class="aa" id="page">
16
+ <div class="bb" id="content">
17
+ <h1 class="aa">Totally awesome</h1>
16
18
  <p>Yes it is</p>
17
19
  </div>
18
- <p class="b">Some Content</p>
19
- <p class="b"></p>
20
+ <p class="bb cc">Some Content</p>
21
+ <p class="bb dd !mine"></p>
22
+ </div>
23
+ <div id="#special">
24
+ </div>
25
+ <div class="some random words" id="random_words">
26
+ Something
20
27
  </div>
21
- <input type="checkbox"/>
28
+ <input id="2checkbox" class="2checkbox" type="checkbox"/>
22
29
  <input type="radio"/>
23
- <input type="text"/>
24
- <input type="file"/>
30
+ <label for="my_text_input">My Text Input</label>
31
+ <input type="text" name="form[my_text_input]" placeholder="my text" id="my_text_input"/>
32
+ <input type="file" id="file" class=".special file"/>
33
+ <input type="hidden" id="hidden_field" value="this is hidden"/>
34
+ <input type="submit" value="click me" title="submit button"/>
35
+ <input type="button" value="don't click me" title="Other button 1" style="line-height: 30px;"/>
25
36
  <a href="#">link</a>
26
37
  <fieldset></fieldset>
27
- <select>
38
+ <select id="select">
28
39
  <option value="a">A</option>
29
40
  <option value="b" disabled>B</option>
30
41
  <option value="c" selected>C</option>
31
42
  </select>
32
43
  <table>
33
44
  <tr><td></td></tr>
34
- </table
45
+ </table>
46
+ <table id="rows">
47
+ <tr>
48
+ <td>A</td><td>B</td><td>C</td>
49
+ </tr>
50
+ <tr>
51
+ <td>D</td><td>E</td><td>F</td>
52
+ </tr>
53
+ </table>
54
+ <table id="cols">
55
+ <tbody>
56
+ <tr>
57
+ <td>A</td><td>D</td>
58
+ </tr>
59
+ <tr>
60
+ <td>B</td><td>E</td>
61
+ </tr>
62
+ <tr>
63
+ <td>C</td><td>F</td>
64
+ </tr>
65
+ </tbody>
66
+ </table>
35
67
  </body>
36
68
  </html>
37
69
  STRING
38
70
  end
39
71
 
40
72
  before do
41
- Capybara.add_selector :custom_selector do
73
+ described_class.add_selector :custom_selector do
42
74
  css { |css_class| "div.#{css_class}" }
43
- filter(:not_empty, boolean: true, default: true, skip_if: :all) { |node, value| value ^ (node.text == '') }
75
+ node_filter(:not_empty, boolean: true, default: true, skip_if: :all) { |node, value| value ^ (node.text == '') }
44
76
  end
77
+
78
+ described_class.add_selector :custom_css_selector do
79
+ css(:name, :other_name) do |selector, name: nil, **|
80
+ selector ||= ''
81
+ selector += "[name='#{name}']" if name
82
+ selector
83
+ end
84
+
85
+ expression_filter(:placeholder) do |expr, val|
86
+ expr + "[placeholder='#{val}']"
87
+ end
88
+
89
+ expression_filter(:value) do |expr, val|
90
+ expr + "[value='#{val}']"
91
+ end
92
+
93
+ expression_filter(:title) do |expr, val|
94
+ expr + "[title='#{val}']"
95
+ end
96
+ end
97
+
98
+ described_class.add_selector :custom_xpath_selector do
99
+ xpath(:valid1, :valid2) { |selector| selector }
100
+ match { |value| value == 'match_me' }
101
+ end
102
+ end
103
+
104
+ it 'supports `filter` as an alias for `node_filter`' do
105
+ expect do
106
+ described_class.add_selector :filter_alias_selector do
107
+ css { |_unused| 'div' }
108
+ filter(:something) { |_node, _value| true }
109
+ end
110
+ end.not_to raise_error
45
111
  end
46
112
 
47
- describe "modify_selector" do
48
- it "allows modifying a selector" do
49
- el = string.find(:custom_selector, 'a')
113
+ describe 'adding a selector' do
114
+ it 'can set default visiblity' do
115
+ described_class.add_selector :hidden_field do
116
+ visible :hidden
117
+ css { |_sel| 'input[type="hidden"]' }
118
+ end
119
+
120
+ expect(string).to have_no_css('input[type="hidden"]')
121
+ expect(string).to have_selector(:hidden_field)
122
+ end
123
+ end
124
+
125
+ describe 'modify_selector' do
126
+ it 'allows modifying a selector' do
127
+ el = string.find(:custom_selector, 'aa')
50
128
  expect(el.tag_name).to eq 'div'
51
- Capybara.modify_selector :custom_selector do
129
+ described_class.modify_selector :custom_selector do
52
130
  css { |css_class| "h1.#{css_class}" }
53
131
  end
54
- el = string.find(:custom_selector, 'a')
132
+ el = string.find(:custom_selector, 'aa')
55
133
  expect(el.tag_name).to eq 'h1'
56
134
  end
57
135
 
58
136
  it "doesn't change existing filters" do
59
- Capybara.modify_selector :custom_selector do
60
- css { |css_class| "p.#{css_class}"}
137
+ described_class.modify_selector :custom_selector do
138
+ css { |css_class| "p.#{css_class}" }
139
+ end
140
+ expect(string).to have_selector(:custom_selector, 'bb', count: 1)
141
+ expect(string).to have_selector(:custom_selector, 'bb', not_empty: false, count: 1)
142
+ expect(string).to have_selector(:custom_selector, 'bb', not_empty: :all, count: 2)
143
+ end
144
+ end
145
+
146
+ describe '::[]' do
147
+ it 'can find a selector' do
148
+ expect(Capybara::Selector[:field]).not_to be_nil
149
+ end
150
+
151
+ it 'raises if no selector found' do
152
+ expect { Capybara::Selector[:no_exist] }.to raise_error(ArgumentError, /Unknown selector type/)
153
+ end
154
+ end
155
+
156
+ describe '::for' do
157
+ it 'finds selector that matches the locator' do
158
+ expect(Capybara::Selector.for('match_me').name).to eq :custom_xpath_selector
159
+ end
160
+
161
+ it 'returns nil if no match' do
162
+ expect(Capybara::Selector.for('nothing')).to be nil
163
+ end
164
+ end
165
+
166
+ describe 'xpath' do
167
+ it 'uses filter names passed in' do
168
+ described_class.add_selector :test do
169
+ xpath(:something, :other) { |_locator| XPath.descendant }
170
+ end
171
+ selector = Capybara::Selector.new :test, config: nil, format: nil
172
+
173
+ expect(selector.expression_filters.keys).to include(:something, :other)
174
+ end
175
+
176
+ it 'gets filter names from block if none passed to xpath method' do
177
+ described_class.add_selector :test do
178
+ xpath { |_locator, valid3:, valid4: nil| "#{valid3} #{valid4}" }
179
+ end
180
+ selector = Capybara::Selector.new :test, config: nil, format: nil
181
+
182
+ expect(selector.expression_filters.keys).to include(:valid3, :valid4)
183
+ end
184
+
185
+ it 'ignores block parameters if names passed in' do
186
+ described_class.add_selector :test do
187
+ xpath(:valid1) { |_locator, valid3:, valid4: nil| "#{valid3} #{valid4}" }
61
188
  end
62
- expect(string).to have_selector(:custom_selector, 'b', count: 1)
63
- expect(string).to have_selector(:custom_selector, 'b', not_empty: false, count: 1)
64
- expect(string).to have_selector(:custom_selector, 'b', not_empty: :all, count: 2)
189
+ selector = Capybara::Selector.new :test, config: nil, format: nil
190
+
191
+ expect(selector.expression_filters.keys).to include(:valid1)
192
+ expect(selector.expression_filters.keys).not_to include(:valid3, :valid4)
193
+ end
194
+ end
195
+
196
+ describe 'css' do
197
+ it "supports filters specified in 'css' definition" do
198
+ expect(string).to have_selector(:custom_css_selector, 'input', name: 'form[my_text_input]')
199
+ expect(string).to have_no_selector(:custom_css_selector, 'input', name: 'form[not_my_text_input]')
200
+ end
201
+
202
+ it 'supports explicitly defined expression filters' do
203
+ expect(string).to have_selector(:custom_css_selector, placeholder: 'my text')
204
+ expect(string).to have_no_selector(:custom_css_selector, placeholder: 'not my text')
205
+ expect(string).to have_selector(:custom_css_selector, value: 'click me', title: 'submit button')
206
+ end
207
+
208
+ it 'uses filter names passed in' do
209
+ described_class.add_selector :test do
210
+ css(:name, :other_name) { |_locator| '' }
211
+ end
212
+ selector = Capybara::Selector.new :test, config: nil, format: nil
213
+
214
+ expect(selector.expression_filters.keys).to include(:name, :other_name)
215
+ end
216
+
217
+ it 'gets filter names from block if none passed to css method' do
218
+ described_class.add_selector :test do
219
+ css { |_locator, valid3:, valid4: nil| "#{valid3} #{valid4}" }
220
+ end
221
+ selector = Capybara::Selector.new :test, config: nil, format: nil
222
+
223
+ expect(selector.expression_filters.keys).to include(:valid3, :valid4)
224
+ end
225
+
226
+ it 'ignores block parameters if names passed in' do
227
+ described_class.add_selector :test do
228
+ css(:valid1) { |_locator, valid3:, valid4: nil| "#{valid3} #{valid4}" }
229
+ end
230
+ selector = Capybara::Selector.new :test, config: nil, format: nil
231
+
232
+ expect(selector.expression_filters.keys).to include(:valid1)
233
+ expect(selector.expression_filters.keys).not_to include(:valid3, :valid4)
65
234
  end
66
235
  end
67
236
 
68
- describe "builtin selectors" do
69
- context "when locator is nil" do
70
- it "devolves to just finding element types" do
237
+ describe 'builtin selectors' do
238
+ context 'when locator is nil' do
239
+ it 'devolves to just finding element types' do
71
240
  selectors = {
72
241
  field: ".//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]",
73
- fieldset: ".//fieldset",
74
- link: ".//a[./@href]",
75
- link_or_button: ".//a[./@href] | .//input[./@type = 'submit' or ./@type = 'reset' or ./@type = 'image' or ./@type = 'button'] | .//button" ,
242
+ fieldset: './/fieldset',
243
+ link: './/a[./@href]',
244
+ link_or_button: ".//a[./@href] | .//input[./@type = 'submit' or ./@type = 'reset' or ./@type = 'image' or ./@type = 'button'] | .//button",
76
245
  fillable_field: ".//*[self::input | self::textarea][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'radio' or ./@type = 'checkbox' or ./@type = 'hidden' or ./@type = 'file')]",
77
246
  radio_button: ".//input[./@type = 'radio']",
78
247
  checkbox: ".//input[./@type = 'checkbox']",
79
- select: ".//select",
80
- option: ".//option",
248
+ select: './/select',
249
+ option: './/option',
81
250
  file_field: ".//input[./@type = 'file']",
82
- table: ".//table"
251
+ table: './/table'
83
252
  }
84
253
  selectors.each do |selector, xpath|
85
- results = string.all(selector,nil).to_a.map &:native
254
+ results = string.all(selector, nil).to_a.map(&:native)
86
255
  expect(results.size).to be > 0
87
256
  expect(results).to eq string.all(:xpath, xpath).to_a.map(&:native)
88
257
  end
89
258
  end
90
259
  end
91
260
 
92
- describe ":option selector" do
93
- it "finds disabled options" do
261
+ context 'with :id option' do
262
+ it 'works with compound css selectors' do
263
+ expect(string.all(:custom_css_selector, 'div, h1', id: 'page').size).to eq 1
264
+ expect(string.all(:custom_css_selector, 'h1, div', id: 'page').size).to eq 1
265
+ end
266
+
267
+ it "works with 'special' characters" do
268
+ expect(string.find(:custom_css_selector, 'div', id: '#special')[:id]).to eq '#special'
269
+ expect(string.find(:custom_css_selector, 'input', id: '2checkbox')[:id]).to eq '2checkbox'
270
+ end
271
+
272
+ it 'accepts XPath expression for xpath based selectors' do
273
+ expect(string.find(:custom_xpath_selector, './/div', id: XPath.contains('peci'))[:id]).to eq '#special'
274
+ expect(string.find(:custom_xpath_selector, './/input', id: XPath.ends_with('box'))[:id]).to eq '2checkbox'
275
+ end
276
+
277
+ it 'errors XPath expression for CSS based selectors' do
278
+ expect { string.find(:custom_css_selector, 'div', id: XPath.contains('peci')) }
279
+ .to raise_error(ArgumentError, /not supported/)
280
+ end
281
+
282
+ it 'accepts Regexp for xpath based selectors' do
283
+ expect(string.find(:custom_xpath_selector, './/div', id: /peci/)[:id]).to eq '#special'
284
+ expect(string.find(:custom_xpath_selector, './/div', id: /pEcI/i)[:id]).to eq '#special'
285
+ end
286
+
287
+ it 'accepts Regexp for css based selectors' do
288
+ expect(string.find(:custom_css_selector, 'div', id: /sp.*al/)[:id]).to eq '#special'
289
+ end
290
+ end
291
+
292
+ context 'with :class option' do
293
+ it 'works with compound css selectors' do
294
+ expect(string.all(:custom_css_selector, 'div, h1', class: 'aa').size).to eq 2
295
+ expect(string.all(:custom_css_selector, 'h1, div', class: 'aa').size).to eq 2
296
+ end
297
+
298
+ it 'handles negated classes' do
299
+ expect(string.all(:custom_css_selector, 'div, p', class: ['bb', '!cc']).size).to eq 2
300
+ expect(string.all(:custom_css_selector, 'div, p', class: ['!cc', '!dd', 'bb']).size).to eq 1
301
+ expect(string.all(:custom_xpath_selector, XPath.descendant(:div, :p), class: ['bb', '!cc']).size).to eq 2
302
+ expect(string.all(:custom_xpath_selector, XPath.descendant(:div, :p), class: ['!cc', '!dd', 'bb']).size).to eq 1
303
+ end
304
+
305
+ it 'handles classes starting with ! by requiring negated negated first' do
306
+ expect(string.all(:custom_css_selector, 'div, p', class: ['!!!mine']).size).to eq 1
307
+ expect(string.all(:custom_xpath_selector, XPath.descendant(:div, :p), class: ['!!!mine']).size).to eq 1
308
+ end
309
+
310
+ it "works with 'special' characters" do
311
+ expect(string.find(:custom_css_selector, 'input', class: '.special')[:id]).to eq 'file'
312
+ expect(string.find(:custom_css_selector, 'input', class: '2checkbox')[:id]).to eq '2checkbox'
313
+ end
314
+
315
+ it 'accepts XPath expression for xpath based selectors' do
316
+ expect(string.find(:custom_xpath_selector, './/div', class: XPath.contains('dom wor'))[:id]).to eq 'random_words'
317
+ expect(string.find(:custom_xpath_selector, './/div', class: XPath.ends_with('words'))[:id]).to eq 'random_words'
318
+ end
319
+
320
+ it 'errors XPath expression for CSS based selectors' do
321
+ expect { string.find(:custom_css_selector, 'div', class: XPath.contains('random')) }
322
+ .to raise_error(ArgumentError, /not supported/)
323
+ end
324
+
325
+ it 'accepts Regexp for XPath based selectors' do
326
+ expect(string.find(:custom_xpath_selector, './/div', class: /dom wor/)[:id]).to eq 'random_words'
327
+ expect(string.find(:custom_xpath_selector, './/div', class: /dOm WoR/i)[:id]).to eq 'random_words'
328
+ end
329
+
330
+ it 'accepts Regexp for CSS based selectors' do
331
+ expect(string.find(:custom_css_selector, 'div', class: /random/)[:id]).to eq 'random_words'
332
+ end
333
+
334
+ it 'accepts Regexp for individual class names for XPath based selectors' do
335
+ expect(string.find(:custom_xpath_selector, './/div', class: [/random/, 'some'])[:id]).to eq 'random_words'
336
+ expect(string.find(:custom_xpath_selector, './/div', class: [/om/, /wor/])[:id]).to eq 'random_words'
337
+ expect { string.find(:custom_xpath_selector, './/div', class: [/not/, /wor/]) }.to raise_error(Capybara::ElementNotFound)
338
+ expect { string.find(:custom_xpath_selector, './/div', class: [/dom wor/]) }.to raise_error(Capybara::ElementNotFound)
339
+ end
340
+
341
+ it 'accepts Regexp for individual class names for CSS based selectors' do
342
+ expect(string.find(:custom_css_selector, 'div', class: [/random/])[:id]).to eq 'random_words'
343
+ expect(string.find(:custom_css_selector, 'div', class: [/om/, /wor/, 'some'])[:id]).to eq 'random_words'
344
+ expect { string.find(:custom_css_selector, 'div', class: [/not/, /wor/]) }.to raise_error(Capybara::ElementNotFound)
345
+ expect { string.find(:custom_css_selector, 'div', class: [/dom wor/]) }.to raise_error(Capybara::ElementNotFound)
346
+ end
347
+ end
348
+
349
+ context 'with :style option' do
350
+ it 'accepts string for CSS based selectors' do
351
+ expect(string.find(:custom_css_selector, 'input', style: 'line-height: 30px;')[:title]).to eq 'Other button 1'
352
+ end
353
+
354
+ it 'accepts Regexp for CSS base selectors' do
355
+ expect(string.find(:custom_css_selector, 'input', style: /30px/)[:title]).to eq 'Other button 1'
356
+ end
357
+ end
358
+
359
+ # :css, :xpath, :id, :field, :fieldset, :link, :button, :link_or_button, :fillable_field, :radio_button, :checkbox, :select,
360
+ # :option, :file_field, :label, :table, :frame
361
+
362
+ describe ':css selector' do
363
+ it 'finds by CSS locator' do
364
+ expect(string.find(:css, 'input#my_text_input')[:name]).to eq 'form[my_text_input]'
365
+ end
366
+ end
367
+
368
+ describe ':xpath selector' do
369
+ it 'finds by XPath locator' do
370
+ expect(string.find(:xpath, './/input[@id="my_text_input"]')[:name]).to eq 'form[my_text_input]'
371
+ end
372
+ end
373
+
374
+ describe ':id selector' do
375
+ it 'finds by locator' do
376
+ expect(string.find(:id, 'my_text_input')[:name]).to eq 'form[my_text_input]'
377
+ expect(string.find(:id, /my_text_input/)[:name]).to eq 'form[my_text_input]'
378
+ expect(string.find(:id, /_text_/)[:name]).to eq 'form[my_text_input]'
379
+ expect(string.find(:id, /i[nmo]/)[:name]).to eq 'form[my_text_input]'
380
+ end
381
+ end
382
+
383
+ describe ':field selector' do
384
+ it 'finds by locator' do
385
+ expect(string.find(:field, 'My Text Input')[:id]).to eq 'my_text_input'
386
+ expect(string.find(:field, 'my_text_input')[:id]).to eq 'my_text_input'
387
+ expect(string.find(:field, 'form[my_text_input]')[:id]).to eq 'my_text_input'
388
+ end
389
+
390
+ it 'finds by id string' do
391
+ expect(string.find(:field, id: 'my_text_input')[:name]).to eq 'form[my_text_input]'
392
+ end
393
+
394
+ it 'finds by id regexp' do
395
+ expect(string.find(:field, id: /my_text_inp/)[:name]).to eq 'form[my_text_input]'
396
+ end
397
+
398
+ it 'finds by name' do
399
+ expect(string.find(:field, name: 'form[my_text_input]')[:id]).to eq 'my_text_input'
400
+ end
401
+
402
+ it 'finds by placeholder' do
403
+ expect(string.find(:field, placeholder: 'my text')[:id]).to eq 'my_text_input'
404
+ end
405
+
406
+ it 'finds by type' do
407
+ expect(string.find(:field, type: 'file')[:id]).to eq 'file'
408
+ expect(string.find(:field, type: 'select')[:id]).to eq 'select'
409
+ end
410
+ end
411
+
412
+ describe ':option selector' do
413
+ it 'finds disabled options' do
94
414
  expect(string.find(:option, disabled: true).value).to eq 'b'
95
415
  end
96
416
 
97
- it "finds selected options" do
417
+ it 'finds selected options' do
98
418
  expect(string.find(:option, selected: true).value).to eq 'c'
99
419
  end
100
420
 
101
- it "finds not selected and not disabled options" do
421
+ it 'finds not selected and not disabled options' do
102
422
  expect(string.find(:option, disabled: false, selected: false).value).to eq 'a'
103
423
  end
104
424
  end
425
+
426
+ describe ':button selector' do
427
+ it 'finds by value' do
428
+ expect(string.find(:button, 'click me').value).to eq 'click me'
429
+ end
430
+
431
+ it 'finds by title' do
432
+ expect(string.find(:button, 'submit button').value).to eq 'click me'
433
+ end
434
+
435
+ it 'includes non-matching parameters in failure message' do
436
+ expect { string.find(:button, 'click me', title: 'click me') }.to raise_error(/with title click me/)
437
+ end
438
+ end
439
+
440
+ describe ':element selector' do
441
+ it 'finds by any attributes' do
442
+ expect(string.find(:element, 'input', type: 'submit').value).to eq 'click me'
443
+ end
444
+
445
+ it 'supports regexp matching' do
446
+ expect(string.find(:element, 'input', type: /sub/).value).to eq 'click me'
447
+ expect(string.find(:element, 'input', title: /sub\w.*button/).value).to eq 'click me'
448
+ expect(string.find(:element, 'input', title: /sub.* b.*ton/).value).to eq 'click me'
449
+ expect(string.find(:element, 'input', title: /sub.*mit.*/).value).to eq 'click me'
450
+ expect(string.find(:element, 'input', title: /^submit button$/).value).to eq 'click me'
451
+ expect(string.find(:element, 'input', title: /^(?:submit|other) button$/).value).to eq 'click me'
452
+ expect(string.find(:element, 'input', title: /SuB.*mIt/i).value).to eq 'click me'
453
+ expect(string.find(:element, 'input', title: /^Su.*Bm.*It/i).value).to eq 'click me'
454
+ expect(string.find(:element, 'input', title: /^Ot.*he.*r b.*\d/i).value).to eq "don't click me"
455
+ end
456
+
457
+ it 'still works with system keys' do
458
+ expect { string.all(:element, 'input', type: 'submit', count: 1) }.not_to raise_error
459
+ end
460
+
461
+ it 'works without element type' do
462
+ expect(string.find(:element, type: 'submit').value).to eq 'click me'
463
+ end
464
+
465
+ it 'validates attribute presence when true' do
466
+ expect(string.find(:element, name: true)[:id]).to eq 'my_text_input'
467
+ end
468
+
469
+ it 'validates attribute absence when false' do
470
+ expect(string.find(:element, 'option', disabled: false, selected: false).value).to eq 'a'
471
+ end
472
+
473
+ it 'includes wildcarded keys in description' do
474
+ expect { string.find(:element, 'input', not_there: 'bad', presence: true, absence: false, count: 1) }
475
+ .to(raise_error do |e|
476
+ expect(e).to be_a(Capybara::ElementNotFound)
477
+ expect(e.message).to include 'not_there => bad'
478
+ expect(e.message).to include 'with presence attribute'
479
+ expect(e.message).to include 'without absence attribute'
480
+ expect(e.message).not_to include 'count 1'
481
+ end)
482
+ end
483
+
484
+ it 'accepts XPath::Expression' do
485
+ expect(string.find(:element, 'input', type: XPath.starts_with('subm')).value).to eq 'click me'
486
+ expect(string.find(:element, 'input', type: XPath.ends_with('ext'))[:type]).to eq 'text'
487
+ expect(string.find(:element, 'input', type: XPath.contains('ckb'))[:type]).to eq 'checkbox'
488
+ expect(string.find(:element, 'input', title: XPath.contains_word('submit'))[:type]).to eq 'submit'
489
+ expect(string.find(:element, 'input', title: XPath.contains_word('button 1'))[:type]).to eq 'button'
490
+ end
491
+ end
492
+
493
+ describe ':link_or_button selector' do
494
+ around do |example|
495
+ described_class.modify_selector(:link_or_button) do
496
+ expression_filter(:random) { |xpath, _| xpath } # do nothing filter
497
+ end
498
+ example.run
499
+ Capybara::Selector[:link_or_button].expression_filters.delete(:random)
500
+ end
501
+
502
+ it 'should not find links when disabled == true' do
503
+ expect(string.all(:link_or_button, disabled: true).size).to eq 0
504
+ end
505
+
506
+ context 'when modified' do
507
+ it 'should still work' do
508
+ filter = Capybara::Selector[:link_or_button].expression_filters[:random]
509
+ allow(filter).to receive(:apply_filter).and_call_original
510
+
511
+ expect(string.find(:link_or_button, 'click me', random: 'blah').value).to eq 'click me'
512
+ expect(filter).to have_received(:apply_filter).with(anything, :random, 'blah', anything)
513
+ end
514
+ end
515
+ end
516
+
517
+ describe ':table selector' do
518
+ it 'finds by rows' do
519
+ expect(string.find(:table, with_rows: [%w[D E F]])[:id]).to eq 'rows'
520
+ end
521
+
522
+ it 'finds by columns' do
523
+ expect(string.find(:table, with_cols: [%w[A B C]])[:id]).to eq 'cols'
524
+ end
525
+ end
105
526
  end
106
527
  end
107
528
  end