capybara 3.32.2

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