capybara 3.0.0 → 3.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (312) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/History.md +891 -12
  4. data/License.txt +1 -1
  5. data/README.md +257 -84
  6. data/lib/capybara/config.rb +29 -10
  7. data/lib/capybara/cucumber.rb +1 -1
  8. data/lib/capybara/driver/base.rb +22 -4
  9. data/lib/capybara/driver/node.rb +38 -9
  10. data/lib/capybara/dsl.rb +9 -7
  11. data/lib/capybara/helpers.rb +57 -8
  12. data/lib/capybara/minitest/spec.rb +185 -84
  13. data/lib/capybara/minitest.rb +264 -145
  14. data/lib/capybara/node/actions.rb +248 -124
  15. data/lib/capybara/node/base.rb +35 -20
  16. data/lib/capybara/node/document.rb +14 -2
  17. data/lib/capybara/node/document_matchers.rb +13 -15
  18. data/lib/capybara/node/element.rb +350 -113
  19. data/lib/capybara/node/finders.rb +104 -82
  20. data/lib/capybara/node/matchers.rb +363 -157
  21. data/lib/capybara/node/simple.rb +54 -15
  22. data/lib/capybara/node/whitespace_normalizer.rb +81 -0
  23. data/lib/capybara/queries/active_element_query.rb +18 -0
  24. data/lib/capybara/queries/ancestor_query.rb +9 -10
  25. data/lib/capybara/queries/base_query.rb +25 -18
  26. data/lib/capybara/queries/current_path_query.rb +16 -6
  27. data/lib/capybara/queries/match_query.rb +11 -0
  28. data/lib/capybara/queries/selector_query.rb +617 -104
  29. data/lib/capybara/queries/sibling_query.rb +9 -7
  30. data/lib/capybara/queries/style_query.rb +45 -0
  31. data/lib/capybara/queries/text_query.rb +40 -22
  32. data/lib/capybara/queries/title_query.rb +2 -2
  33. data/lib/capybara/rack_test/browser.rb +106 -31
  34. data/lib/capybara/rack_test/driver.rb +16 -7
  35. data/lib/capybara/rack_test/errors.rb +6 -0
  36. data/lib/capybara/rack_test/form.rb +74 -49
  37. data/lib/capybara/rack_test/node.rb +120 -47
  38. data/lib/capybara/rails.rb +1 -1
  39. data/lib/capybara/registration_container.rb +41 -0
  40. data/lib/capybara/registrations/drivers.rb +42 -0
  41. data/lib/capybara/registrations/patches/puma_ssl.rb +29 -0
  42. data/lib/capybara/registrations/servers.rb +66 -0
  43. data/lib/capybara/result.rb +87 -53
  44. data/lib/capybara/rspec/features.rb +8 -10
  45. data/lib/capybara/rspec/matcher_proxies.rb +39 -18
  46. data/lib/capybara/rspec/matchers/base.rb +113 -0
  47. data/lib/capybara/rspec/matchers/become_closed.rb +33 -0
  48. data/lib/capybara/rspec/matchers/compound.rb +88 -0
  49. data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
  50. data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
  51. data/lib/capybara/rspec/matchers/have_current_path.rb +29 -0
  52. data/lib/capybara/rspec/matchers/have_selector.rb +69 -0
  53. data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
  54. data/lib/capybara/rspec/matchers/have_text.rb +33 -0
  55. data/lib/capybara/rspec/matchers/have_title.rb +29 -0
  56. data/lib/capybara/rspec/matchers/match_selector.rb +27 -0
  57. data/lib/capybara/rspec/matchers/match_style.rb +43 -0
  58. data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
  59. data/lib/capybara/rspec/matchers.rb +142 -315
  60. data/lib/capybara/rspec.rb +3 -2
  61. data/lib/capybara/selector/builders/css_builder.rb +84 -0
  62. data/lib/capybara/selector/builders/xpath_builder.rb +71 -0
  63. data/lib/capybara/selector/css.rb +85 -8
  64. data/lib/capybara/selector/definition/button.rb +68 -0
  65. data/lib/capybara/selector/definition/checkbox.rb +26 -0
  66. data/lib/capybara/selector/definition/css.rb +10 -0
  67. data/lib/capybara/selector/definition/datalist_input.rb +35 -0
  68. data/lib/capybara/selector/definition/datalist_option.rb +25 -0
  69. data/lib/capybara/selector/definition/element.rb +28 -0
  70. data/lib/capybara/selector/definition/field.rb +40 -0
  71. data/lib/capybara/selector/definition/fieldset.rb +14 -0
  72. data/lib/capybara/selector/definition/file_field.rb +13 -0
  73. data/lib/capybara/selector/definition/fillable_field.rb +33 -0
  74. data/lib/capybara/selector/definition/frame.rb +17 -0
  75. data/lib/capybara/selector/definition/id.rb +6 -0
  76. data/lib/capybara/selector/definition/label.rb +62 -0
  77. data/lib/capybara/selector/definition/link.rb +55 -0
  78. data/lib/capybara/selector/definition/link_or_button.rb +16 -0
  79. data/lib/capybara/selector/definition/option.rb +27 -0
  80. data/lib/capybara/selector/definition/radio_button.rb +27 -0
  81. data/lib/capybara/selector/definition/select.rb +81 -0
  82. data/lib/capybara/selector/definition/table.rb +109 -0
  83. data/lib/capybara/selector/definition/table_row.rb +21 -0
  84. data/lib/capybara/selector/definition/xpath.rb +5 -0
  85. data/lib/capybara/selector/definition.rb +280 -0
  86. data/lib/capybara/selector/filter.rb +1 -0
  87. data/lib/capybara/selector/filter_set.rb +72 -28
  88. data/lib/capybara/selector/filters/base.rb +45 -2
  89. data/lib/capybara/selector/filters/expression_filter.rb +5 -6
  90. data/lib/capybara/selector/filters/locator_filter.rb +29 -0
  91. data/lib/capybara/selector/filters/node_filter.rb +18 -4
  92. data/lib/capybara/selector/regexp_disassembler.rb +211 -0
  93. data/lib/capybara/selector/selector.rb +89 -200
  94. data/lib/capybara/selector/xpath_extensions.rb +17 -0
  95. data/lib/capybara/selector.rb +474 -534
  96. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -0
  97. data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -0
  98. data/lib/capybara/selenium/atoms/src/getAttribute.js +161 -0
  99. data/lib/capybara/selenium/atoms/src/isDisplayed.js +454 -0
  100. data/lib/capybara/selenium/driver.rb +270 -245
  101. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +117 -0
  102. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +128 -0
  103. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +84 -0
  104. data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +26 -0
  105. data/lib/capybara/selenium/driver_specializations/safari_driver.rb +24 -0
  106. data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
  107. data/lib/capybara/selenium/extensions/find.rb +110 -0
  108. data/lib/capybara/selenium/extensions/html5_drag.rb +229 -0
  109. data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
  110. data/lib/capybara/selenium/extensions/scroll.rb +76 -0
  111. data/lib/capybara/selenium/node.rb +460 -170
  112. data/lib/capybara/selenium/nodes/chrome_node.rb +125 -0
  113. data/lib/capybara/selenium/nodes/edge_node.rb +110 -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/atoms.rb +18 -0
  118. data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
  119. data/lib/capybara/selenium/patches/logs.rb +45 -0
  120. data/lib/capybara/selenium/patches/pause_duration_fix.rb +9 -0
  121. data/lib/capybara/selenium/patches/persistent_client.rb +20 -0
  122. data/lib/capybara/server/animation_disabler.rb +80 -0
  123. data/lib/capybara/server/checker.rb +44 -0
  124. data/lib/capybara/server/middleware.rb +71 -0
  125. data/lib/capybara/server.rb +58 -67
  126. data/lib/capybara/session/config.rb +40 -6
  127. data/lib/capybara/session/matchers.rb +26 -19
  128. data/lib/capybara/session.rb +252 -194
  129. data/lib/capybara/spec/public/jquery.js +5 -5
  130. data/lib/capybara/spec/public/offset.js +6 -0
  131. data/lib/capybara/spec/public/test.js +126 -8
  132. data/lib/capybara/spec/session/accept_alert_spec.rb +11 -11
  133. data/lib/capybara/spec/session/accept_confirm_spec.rb +3 -3
  134. data/lib/capybara/spec/session/accept_prompt_spec.rb +9 -10
  135. data/lib/capybara/spec/session/active_element_spec.rb +31 -0
  136. data/lib/capybara/spec/session/all_spec.rb +135 -44
  137. data/lib/capybara/spec/session/ancestor_spec.rb +24 -19
  138. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +67 -38
  139. data/lib/capybara/spec/session/{assert_current_path.rb → assert_current_path_spec.rb} +20 -18
  140. data/lib/capybara/spec/session/assert_selector_spec.rb +143 -0
  141. data/lib/capybara/spec/session/assert_style_spec.rb +26 -0
  142. data/lib/capybara/spec/session/{assert_text.rb → assert_text_spec.rb} +76 -52
  143. data/lib/capybara/spec/session/{assert_title.rb → assert_title_spec.rb} +12 -12
  144. data/lib/capybara/spec/session/attach_file_spec.rb +126 -72
  145. data/lib/capybara/spec/session/body_spec.rb +11 -13
  146. data/lib/capybara/spec/session/check_spec.rb +112 -51
  147. data/lib/capybara/spec/session/choose_spec.rb +62 -30
  148. data/lib/capybara/spec/session/click_button_spec.rb +227 -161
  149. data/lib/capybara/spec/session/click_link_or_button_spec.rb +49 -30
  150. data/lib/capybara/spec/session/click_link_spec.rb +89 -55
  151. data/lib/capybara/spec/session/current_scope_spec.rb +8 -8
  152. data/lib/capybara/spec/session/current_url_spec.rb +44 -37
  153. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +3 -3
  154. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +2 -2
  155. data/lib/capybara/spec/session/element/{assert_match_selector.rb → assert_match_selector_spec.rb} +11 -9
  156. data/lib/capybara/spec/session/element/match_css_spec.rb +18 -10
  157. data/lib/capybara/spec/session/element/match_xpath_spec.rb +8 -6
  158. data/lib/capybara/spec/session/element/matches_selector_spec.rb +70 -56
  159. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +7 -7
  160. data/lib/capybara/spec/session/evaluate_script_spec.rb +28 -8
  161. data/lib/capybara/spec/session/execute_script_spec.rb +8 -7
  162. data/lib/capybara/spec/session/fill_in_spec.rb +110 -44
  163. data/lib/capybara/spec/session/find_button_spec.rb +23 -23
  164. data/lib/capybara/spec/session/find_by_id_spec.rb +8 -8
  165. data/lib/capybara/spec/session/find_field_spec.rb +33 -31
  166. data/lib/capybara/spec/session/find_link_spec.rb +42 -14
  167. data/lib/capybara/spec/session/find_spec.rb +251 -142
  168. data/lib/capybara/spec/session/first_spec.rb +45 -44
  169. data/lib/capybara/spec/session/frame/frame_title_spec.rb +6 -6
  170. data/lib/capybara/spec/session/frame/frame_url_spec.rb +6 -6
  171. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +32 -20
  172. data/lib/capybara/spec/session/frame/within_frame_spec.rb +46 -19
  173. data/lib/capybara/spec/session/go_back_spec.rb +1 -1
  174. data/lib/capybara/spec/session/go_forward_spec.rb +1 -1
  175. data/lib/capybara/spec/session/has_all_selectors_spec.rb +23 -23
  176. data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
  177. data/lib/capybara/spec/session/has_any_selectors_spec.rb +29 -0
  178. data/lib/capybara/spec/session/has_button_spec.rb +98 -12
  179. data/lib/capybara/spec/session/has_css_spec.rb +271 -137
  180. data/lib/capybara/spec/session/has_current_path_spec.rb +50 -35
  181. data/lib/capybara/spec/session/has_element_spec.rb +47 -0
  182. data/lib/capybara/spec/session/has_field_spec.rb +137 -58
  183. data/lib/capybara/spec/session/has_link_spec.rb +46 -6
  184. data/lib/capybara/spec/session/has_none_selectors_spec.rb +33 -31
  185. data/lib/capybara/spec/session/has_select_spec.rb +84 -50
  186. data/lib/capybara/spec/session/has_selector_spec.rb +117 -69
  187. data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
  188. data/lib/capybara/spec/session/has_table_spec.rb +181 -4
  189. data/lib/capybara/spec/session/has_text_spec.rb +101 -53
  190. data/lib/capybara/spec/session/has_title_spec.rb +19 -14
  191. data/lib/capybara/spec/session/has_xpath_spec.rb +56 -38
  192. data/lib/capybara/spec/session/{headers.rb → headers_spec.rb} +1 -1
  193. data/lib/capybara/spec/session/html_spec.rb +13 -6
  194. data/lib/capybara/spec/session/matches_style_spec.rb +37 -0
  195. data/lib/capybara/spec/session/node_spec.rb +958 -122
  196. data/lib/capybara/spec/session/node_wrapper_spec.rb +15 -12
  197. data/lib/capybara/spec/session/refresh_spec.rb +9 -7
  198. data/lib/capybara/spec/session/reset_session_spec.rb +65 -37
  199. data/lib/capybara/spec/session/{response_code.rb → response_code_spec.rb} +1 -1
  200. data/lib/capybara/spec/session/save_and_open_page_spec.rb +2 -2
  201. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +5 -4
  202. data/lib/capybara/spec/session/save_page_spec.rb +41 -38
  203. data/lib/capybara/spec/session/save_screenshot_spec.rb +13 -11
  204. data/lib/capybara/spec/session/screenshot_spec.rb +2 -2
  205. data/lib/capybara/spec/session/scroll_spec.rb +119 -0
  206. data/lib/capybara/spec/session/select_spec.rb +102 -76
  207. data/lib/capybara/spec/session/selectors_spec.rb +51 -18
  208. data/lib/capybara/spec/session/sibling_spec.rb +9 -9
  209. data/lib/capybara/spec/session/text_spec.rb +26 -24
  210. data/lib/capybara/spec/session/title_spec.rb +8 -6
  211. data/lib/capybara/spec/session/uncheck_spec.rb +41 -22
  212. data/lib/capybara/spec/session/unselect_spec.rb +37 -37
  213. data/lib/capybara/spec/session/visit_spec.rb +79 -53
  214. data/lib/capybara/spec/session/window/become_closed_spec.rb +22 -19
  215. data/lib/capybara/spec/session/window/current_window_spec.rb +4 -3
  216. data/lib/capybara/spec/session/window/open_new_window_spec.rb +4 -3
  217. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +25 -21
  218. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +10 -5
  219. data/lib/capybara/spec/session/window/window_spec.rb +88 -54
  220. data/lib/capybara/spec/session/window/windows_spec.rb +11 -8
  221. data/lib/capybara/spec/session/window/within_window_spec.rb +17 -16
  222. data/lib/capybara/spec/session/within_spec.rb +82 -44
  223. data/lib/capybara/spec/spec_helper.rb +46 -52
  224. data/lib/capybara/spec/test_app.rb +148 -41
  225. data/lib/capybara/spec/views/animated.erb +49 -0
  226. data/lib/capybara/spec/views/form.erb +156 -42
  227. data/lib/capybara/spec/views/frame_child.erb +4 -3
  228. data/lib/capybara/spec/views/frame_one.erb +2 -1
  229. data/lib/capybara/spec/views/frame_parent.erb +1 -1
  230. data/lib/capybara/spec/views/frame_two.erb +1 -1
  231. data/lib/capybara/spec/views/initial_alert.erb +2 -1
  232. data/lib/capybara/spec/views/layout.erb +10 -0
  233. data/lib/capybara/spec/views/obscured.erb +47 -0
  234. data/lib/capybara/spec/views/offset.erb +33 -0
  235. data/lib/capybara/spec/views/path.erb +2 -2
  236. data/lib/capybara/spec/views/popup_one.erb +1 -1
  237. data/lib/capybara/spec/views/popup_two.erb +1 -1
  238. data/lib/capybara/spec/views/react.erb +45 -0
  239. data/lib/capybara/spec/views/scroll.erb +21 -0
  240. data/lib/capybara/spec/views/spatial.erb +31 -0
  241. data/lib/capybara/spec/views/tables.erb +68 -1
  242. data/lib/capybara/spec/views/with_animation.erb +81 -0
  243. data/lib/capybara/spec/views/with_base_tag.erb +2 -2
  244. data/lib/capybara/spec/views/with_dragula.erb +24 -0
  245. data/lib/capybara/spec/views/with_fixed_header_footer.erb +2 -1
  246. data/lib/capybara/spec/views/with_hover.erb +3 -2
  247. data/lib/capybara/spec/views/with_hover1.erb +10 -0
  248. data/lib/capybara/spec/views/with_html.erb +46 -11
  249. data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
  250. data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
  251. data/lib/capybara/spec/views/with_js.erb +30 -5
  252. data/lib/capybara/spec/views/with_jstree.erb +26 -0
  253. data/lib/capybara/spec/views/with_namespace.erb +21 -0
  254. data/lib/capybara/spec/views/with_scope.erb +2 -2
  255. data/lib/capybara/spec/views/with_scope_other.erb +6 -0
  256. data/lib/capybara/spec/views/with_shadow.erb +31 -0
  257. data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
  258. data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
  259. data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
  260. data/lib/capybara/spec/views/with_windows.erb +1 -1
  261. data/lib/capybara/spec/views/within_frames.erb +1 -1
  262. data/lib/capybara/version.rb +1 -1
  263. data/lib/capybara/window.rb +32 -26
  264. data/lib/capybara.rb +128 -104
  265. data/spec/basic_node_spec.rb +59 -34
  266. data/spec/capybara_spec.rb +65 -51
  267. data/spec/counter_spec.rb +35 -0
  268. data/spec/css_builder_spec.rb +101 -0
  269. data/spec/css_splitter_spec.rb +38 -0
  270. data/spec/dsl_spec.rb +84 -55
  271. data/spec/filter_set_spec.rb +24 -7
  272. data/spec/fixtures/certificate.pem +25 -0
  273. data/spec/fixtures/key.pem +27 -0
  274. data/spec/fixtures/selenium_driver_rspec_failure.rb +5 -5
  275. data/spec/fixtures/selenium_driver_rspec_success.rb +5 -5
  276. data/spec/minitest_spec.rb +49 -5
  277. data/spec/minitest_spec_spec.rb +92 -62
  278. data/spec/per_session_config_spec.rb +6 -6
  279. data/spec/rack_test_spec.rb +183 -115
  280. data/spec/regexp_dissassembler_spec.rb +250 -0
  281. data/spec/result_spec.rb +99 -39
  282. data/spec/rspec/features_spec.rb +28 -25
  283. data/spec/rspec/scenarios_spec.rb +10 -6
  284. data/spec/rspec/shared_spec_matchers.rb +418 -364
  285. data/spec/rspec/views_spec.rb +4 -3
  286. data/spec/rspec_matchers_spec.rb +35 -10
  287. data/spec/rspec_spec.rb +109 -85
  288. data/spec/sauce_spec_chrome.rb +43 -0
  289. data/spec/selector_spec.rb +392 -62
  290. data/spec/selenium_spec_chrome.rb +183 -41
  291. data/spec/selenium_spec_chrome_remote.rb +96 -0
  292. data/spec/selenium_spec_edge.rb +41 -8
  293. data/spec/selenium_spec_firefox.rb +228 -0
  294. data/spec/selenium_spec_firefox_remote.rb +94 -0
  295. data/spec/selenium_spec_ie.rb +129 -11
  296. data/spec/selenium_spec_safari.rb +162 -0
  297. data/spec/server_spec.rb +192 -81
  298. data/spec/session_spec.rb +52 -16
  299. data/spec/shared_selenium_node.rb +79 -0
  300. data/spec/shared_selenium_session.rb +460 -123
  301. data/spec/spec_helper.rb +124 -2
  302. data/spec/whitespace_normalizer_spec.rb +54 -0
  303. data/spec/xpath_builder_spec.rb +93 -0
  304. metadata +344 -45
  305. data/.yard/templates_custom/default/class/html/selectors.erb +0 -38
  306. data/.yard/templates_custom/default/class/html/setup.rb +0 -17
  307. data/.yard/yard_extensions.rb +0 -78
  308. data/lib/capybara/rspec/compound.rb +0 -90
  309. data/lib/capybara/spec/session/assert_selector.rb +0 -149
  310. data/lib/capybara/spec/session/source_spec.rb +0 -0
  311. data/lib/capybara/spec/views/with_title.erb +0 -5
  312. data/spec/selenium_spec_marionette.rb +0 -143
@@ -3,30 +3,36 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  RSpec.describe Capybara do
6
+ include Capybara::RSpecMatchers
6
7
  describe 'Selectors' do
7
8
  let :string do
8
- Capybara.string <<-STRING
9
+ described_class.string <<-STRING
9
10
  <html>
10
11
  <head>
11
12
  <title>selectors</title>
12
13
  </head>
13
14
  <body>
14
- <div class="a" id="page">
15
- <div class="b" id="content">
16
- <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>
17
18
  <p>Yes it is</p>
18
19
  </div>
19
- <p class="b">Some Content</p>
20
- <p class="b"></p>
20
+ <p class="bb cc">Some Content</p>
21
+ <p class="bb dd !mine"></p>
21
22
  </div>
22
23
  <div id="#special">
23
24
  </div>
25
+ <div class="some random words" id="random_words">
26
+ Something
27
+ </div>
24
28
  <input id="2checkbox" class="2checkbox" type="checkbox"/>
25
29
  <input type="radio"/>
26
30
  <label for="my_text_input">My Text Input</label>
27
31
  <input type="text" name="form[my_text_input]" placeholder="my text" id="my_text_input"/>
28
32
  <input type="file" id="file" class=".special file"/>
29
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;"/>
30
36
  <a href="#">link</a>
31
37
  <fieldset></fieldset>
32
38
  <select id="select">
@@ -36,71 +42,213 @@ RSpec.describe Capybara do
36
42
  </select>
37
43
  <table>
38
44
  <tr><td></td></tr>
39
- </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>
40
67
  </body>
41
68
  </html>
42
69
  STRING
43
70
  end
44
71
 
45
72
  before do
46
- Capybara.add_selector :custom_selector do
73
+ described_class.add_selector :custom_selector do
47
74
  css { |css_class| "div.#{css_class}" }
48
- 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 == '') }
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
49
96
  end
50
97
 
51
- Capybara.add_selector :custom_css_selector do
52
- css { |selector| selector }
98
+ described_class.add_selector :custom_xpath_selector do
99
+ xpath(:valid1, :valid2) { |selector| selector }
100
+ match { |value| value == 'match_me' }
53
101
  end
54
102
  end
55
103
 
56
- describe "adding a selector" do
57
- it "can set default visiblity" do
58
- Capybara.add_selector :hidden_field do
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
111
+ end
112
+
113
+ describe 'adding a selector' do
114
+ it 'can set default visibility' do
115
+ described_class.add_selector :hidden_field do
59
116
  visible :hidden
60
117
  css { |_sel| 'input[type="hidden"]' }
61
118
  end
62
119
 
63
- expect(string).to have_no_css('input[type="hidden"]')
120
+ expect(string).to have_no_css('input[type="hidden"]') # rubocop:disable Capybara/SpecificMatcher
64
121
  expect(string).to have_selector(:hidden_field)
65
122
  end
66
123
  end
67
124
 
68
- describe "modify_selector" do
69
- it "allows modifying a selector" do
70
- el = string.find(:custom_selector, 'a')
125
+ describe 'modify_selector' do
126
+ it 'allows modifying a selector' do
127
+ el = string.find(:custom_selector, 'aa')
71
128
  expect(el.tag_name).to eq 'div'
72
- Capybara.modify_selector :custom_selector do
129
+ described_class.modify_selector :custom_selector do
73
130
  css { |css_class| "h1.#{css_class}" }
74
131
  end
75
- el = string.find(:custom_selector, 'a')
132
+ el = string.find(:custom_selector, 'aa')
76
133
  expect(el.tag_name).to eq 'h1'
77
134
  end
78
135
 
79
136
  it "doesn't change existing filters" do
80
- Capybara.modify_selector :custom_selector do
137
+ described_class.modify_selector :custom_selector do
81
138
  css { |css_class| "p.#{css_class}" }
82
139
  end
83
- expect(string).to have_selector(:custom_selector, 'b', count: 1)
84
- expect(string).to have_selector(:custom_selector, 'b', not_empty: false, count: 1)
85
- expect(string).to have_selector(:custom_selector, 'b', not_empty: :all, count: 2)
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}" }
188
+ end
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)
86
193
  end
87
194
  end
88
195
 
89
- describe "builtin selectors" do
90
- context "when locator is nil" do
91
- it "devolves to just finding element types" do
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)
234
+ end
235
+ end
236
+
237
+ describe 'builtin selectors' do
238
+ context 'when locator is nil' do
239
+ it 'devolves to just finding element types' do
92
240
  selectors = {
93
241
  field: ".//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]",
94
- fieldset: ".//fieldset",
95
- link: ".//a[./@href]",
242
+ fieldset: './/fieldset',
243
+ link: './/a[./@href]',
96
244
  link_or_button: ".//a[./@href] | .//input[./@type = 'submit' or ./@type = 'reset' or ./@type = 'image' or ./@type = 'button'] | .//button",
97
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')]",
98
246
  radio_button: ".//input[./@type = 'radio']",
99
247
  checkbox: ".//input[./@type = 'checkbox']",
100
- select: ".//select",
101
- option: ".//option",
248
+ select: './/select',
249
+ option: './/option',
102
250
  file_field: ".//input[./@type = 'file']",
103
- table: ".//table"
251
+ table: './/table'
104
252
  }
105
253
  selectors.each do |selector, xpath|
106
254
  results = string.all(selector, nil).to_a.map(&:native)
@@ -110,89 +258,271 @@ RSpec.describe Capybara do
110
258
  end
111
259
  end
112
260
 
113
- context "with :id option" do
114
- it "works with compound css selectors" do
115
- expect(string.all(:custom_css_selector, "div, h1", id: 'page').size).to eq 1
116
- expect(string.all(:custom_css_selector, "h1, div", id: 'page').size).to eq 1
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
117
265
  end
118
266
 
119
267
  it "works with 'special' characters" do
120
- expect(string.find(:custom_css_selector, "div", id: "#special")[:id]).to eq '#special'
121
- expect(string.find(:custom_css_selector, "input", id: "2checkbox")[:id]).to eq '2checkbox'
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'
122
289
  end
123
290
  end
124
291
 
125
- context "with :class option" do
126
- it "works with compound css selectors" do
127
- expect(string.all(:custom_css_selector, "div, h1", class: 'a').size).to eq 2
128
- expect(string.all(:custom_css_selector, "h1, div", class: 'a').size).to eq 2
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
129
308
  end
130
309
 
131
310
  it "works with 'special' characters" do
132
- expect(string.find(:custom_css_selector, "input", class: ".special")[:id]).to eq 'file'
133
- expect(string.find(:custom_css_selector, "input", class: "2checkbox")[:id]).to eq '2checkbox'
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'
134
356
  end
135
357
  end
136
358
 
137
359
  # :css, :xpath, :id, :field, :fieldset, :link, :button, :link_or_button, :fillable_field, :radio_button, :checkbox, :select,
138
360
  # :option, :file_field, :label, :table, :frame
139
361
 
140
- describe ":css selector" do
141
- it "finds by CSS locator" do
142
- expect(string.find(:css, "input#my_text_input")[:name]).to eq 'form[my_text_input]'
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]'
143
365
  end
144
366
  end
145
367
 
146
- describe ":xpath selector" do
147
- it "finds by XPath locator" do
368
+ describe ':xpath selector' do
369
+ it 'finds by XPath locator' do
148
370
  expect(string.find(:xpath, './/input[@id="my_text_input"]')[:name]).to eq 'form[my_text_input]'
149
371
  end
150
372
  end
151
373
 
152
- describe ":id selector" do
153
- it "finds by locator" do
154
- expect(string.find(:id, "my_text_input")[:name]).to eq 'form[my_text_input]'
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]'
155
380
  end
156
381
  end
157
382
 
158
- describe ":field selector" do
159
- it "finds by locator" do
383
+ describe ':field selector' do
384
+ it 'finds by locator' do
160
385
  expect(string.find(:field, 'My Text Input')[:id]).to eq 'my_text_input'
161
386
  expect(string.find(:field, 'my_text_input')[:id]).to eq 'my_text_input'
162
387
  expect(string.find(:field, 'form[my_text_input]')[:id]).to eq 'my_text_input'
163
388
  end
164
389
 
165
- it "finds by id" do
390
+ it 'finds by id string' do
166
391
  expect(string.find(:field, id: 'my_text_input')[:name]).to eq 'form[my_text_input]'
167
392
  end
168
393
 
169
- it "finds by name" do
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
170
399
  expect(string.find(:field, name: 'form[my_text_input]')[:id]).to eq 'my_text_input'
171
400
  end
172
401
 
173
- it "finds by placeholder" do
402
+ it 'finds by placeholder' do
174
403
  expect(string.find(:field, placeholder: 'my text')[:id]).to eq 'my_text_input'
175
404
  end
176
405
 
177
- it "finds by type" do
406
+ it 'finds by type' do
178
407
  expect(string.find(:field, type: 'file')[:id]).to eq 'file'
179
408
  expect(string.find(:field, type: 'select')[:id]).to eq 'select'
180
409
  end
181
410
  end
182
411
 
183
- describe ":option selector" do
184
- it "finds disabled options" do
412
+ describe ':option selector' do
413
+ it 'finds disabled options' do
185
414
  expect(string.find(:option, disabled: true).value).to eq 'b'
186
415
  end
187
416
 
188
- it "finds selected options" do
417
+ it 'finds selected options' do
189
418
  expect(string.find(:option, selected: true).value).to eq 'c'
190
419
  end
191
420
 
192
- it "finds not selected and not disabled options" do
421
+ it 'finds not selected and not disabled options' do
193
422
  expect(string.find(:option, disabled: false, selected: false).value).to eq 'a'
194
423
  end
195
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.all(: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
196
526
  end
197
527
  end
198
528
  end