capybara 3.0.0 → 3.36.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 (308) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/History.md +777 -10
  4. data/License.txt +1 -1
  5. data/README.md +75 -58
  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 +34 -9
  10. data/lib/capybara/dsl.rb +14 -6
  11. data/lib/capybara/helpers.rb +52 -7
  12. data/lib/capybara/minitest/spec.rb +173 -84
  13. data/lib/capybara/minitest.rb +250 -144
  14. data/lib/capybara/node/actions.rb +248 -124
  15. data/lib/capybara/node/base.rb +34 -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 +339 -113
  19. data/lib/capybara/node/finders.rb +95 -82
  20. data/lib/capybara/node/matchers.rb +338 -157
  21. data/lib/capybara/node/simple.rb +54 -15
  22. data/lib/capybara/queries/active_element_query.rb +18 -0
  23. data/lib/capybara/queries/ancestor_query.rb +9 -10
  24. data/lib/capybara/queries/base_query.rb +25 -18
  25. data/lib/capybara/queries/current_path_query.rb +16 -6
  26. data/lib/capybara/queries/match_query.rb +11 -0
  27. data/lib/capybara/queries/selector_query.rb +600 -103
  28. data/lib/capybara/queries/sibling_query.rb +9 -7
  29. data/lib/capybara/queries/style_query.rb +45 -0
  30. data/lib/capybara/queries/text_query.rb +40 -22
  31. data/lib/capybara/queries/title_query.rb +2 -2
  32. data/lib/capybara/rack_test/browser.rb +47 -28
  33. data/lib/capybara/rack_test/driver.rb +12 -3
  34. data/lib/capybara/rack_test/errors.rb +6 -0
  35. data/lib/capybara/rack_test/form.rb +53 -50
  36. data/lib/capybara/rack_test/node.rb +114 -37
  37. data/lib/capybara/rails.rb +1 -1
  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 +86 -52
  43. data/lib/capybara/rspec/features.rb +8 -10
  44. data/lib/capybara/rspec/matcher_proxies.rb +39 -18
  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 +138 -316
  59. data/lib/capybara/rspec.rb +3 -2
  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 +89 -12
  63. data/lib/capybara/selector/definition/button.rb +68 -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 +279 -0
  85. data/lib/capybara/selector/filter.rb +1 -0
  86. data/lib/capybara/selector/filter_set.rb +72 -27
  87. data/lib/capybara/selector/filters/base.rb +45 -2
  88. data/lib/capybara/selector/filters/expression_filter.rb +5 -6
  89. data/lib/capybara/selector/filters/locator_filter.rb +29 -0
  90. data/lib/capybara/selector/filters/node_filter.rb +18 -4
  91. data/lib/capybara/selector/regexp_disassembler.rb +214 -0
  92. data/lib/capybara/selector/selector.rb +85 -200
  93. data/lib/capybara/selector/xpath_extensions.rb +17 -0
  94. data/lib/capybara/selector.rb +226 -537
  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 +296 -238
  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 +436 -166
  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 +69 -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 +58 -67
  127. data/lib/capybara/session/config.rb +38 -6
  128. data/lib/capybara/session/matchers.rb +26 -19
  129. data/lib/capybara/session.rb +258 -190
  130. data/lib/capybara/spec/public/jquery.js +5 -5
  131. data/lib/capybara/spec/public/offset.js +6 -0
  132. data/lib/capybara/spec/public/test.js +122 -8
  133. data/lib/capybara/spec/session/accept_alert_spec.rb +11 -11
  134. data/lib/capybara/spec/session/accept_confirm_spec.rb +3 -3
  135. data/lib/capybara/spec/session/accept_prompt_spec.rb +9 -10
  136. data/lib/capybara/spec/session/active_element_spec.rb +31 -0
  137. data/lib/capybara/spec/session/all_spec.rb +135 -42
  138. data/lib/capybara/spec/session/ancestor_spec.rb +24 -19
  139. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +67 -38
  140. data/lib/capybara/spec/session/{assert_current_path.rb → assert_current_path_spec.rb} +20 -18
  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.rb → assert_text_spec.rb} +62 -38
  144. data/lib/capybara/spec/session/{assert_title.rb → assert_title_spec.rb} +12 -12
  145. data/lib/capybara/spec/session/attach_file_spec.rb +120 -72
  146. data/lib/capybara/spec/session/body_spec.rb +11 -13
  147. data/lib/capybara/spec/session/check_spec.rb +111 -51
  148. data/lib/capybara/spec/session/choose_spec.rb +62 -30
  149. data/lib/capybara/spec/session/click_button_spec.rb +227 -161
  150. data/lib/capybara/spec/session/click_link_or_button_spec.rb +49 -30
  151. data/lib/capybara/spec/session/click_link_spec.rb +78 -55
  152. data/lib/capybara/spec/session/current_scope_spec.rb +7 -7
  153. data/lib/capybara/spec/session/current_url_spec.rb +44 -37
  154. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +3 -3
  155. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +2 -2
  156. data/lib/capybara/spec/session/element/{assert_match_selector.rb → assert_match_selector_spec.rb} +11 -9
  157. data/lib/capybara/spec/session/element/match_css_spec.rb +18 -10
  158. data/lib/capybara/spec/session/element/match_xpath_spec.rb +8 -6
  159. data/lib/capybara/spec/session/element/matches_selector_spec.rb +70 -56
  160. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +7 -7
  161. data/lib/capybara/spec/session/evaluate_script_spec.rb +28 -8
  162. data/lib/capybara/spec/session/execute_script_spec.rb +8 -7
  163. data/lib/capybara/spec/session/fill_in_spec.rb +104 -44
  164. data/lib/capybara/spec/session/find_button_spec.rb +23 -23
  165. data/lib/capybara/spec/session/find_by_id_spec.rb +8 -8
  166. data/lib/capybara/spec/session/find_field_spec.rb +33 -31
  167. data/lib/capybara/spec/session/find_link_spec.rb +32 -14
  168. data/lib/capybara/spec/session/find_spec.rb +236 -141
  169. data/lib/capybara/spec/session/first_spec.rb +44 -43
  170. data/lib/capybara/spec/session/frame/frame_title_spec.rb +6 -6
  171. data/lib/capybara/spec/session/frame/frame_url_spec.rb +6 -6
  172. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +32 -20
  173. data/lib/capybara/spec/session/frame/within_frame_spec.rb +44 -19
  174. data/lib/capybara/spec/session/go_back_spec.rb +1 -1
  175. data/lib/capybara/spec/session/go_forward_spec.rb +1 -1
  176. data/lib/capybara/spec/session/has_all_selectors_spec.rb +18 -18
  177. data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
  178. data/lib/capybara/spec/session/has_any_selectors_spec.rb +29 -0
  179. data/lib/capybara/spec/session/has_button_spec.rb +92 -12
  180. data/lib/capybara/spec/session/has_css_spec.rb +271 -137
  181. data/lib/capybara/spec/session/has_current_path_spec.rb +48 -33
  182. data/lib/capybara/spec/session/has_field_spec.rb +137 -58
  183. data/lib/capybara/spec/session/has_link_spec.rb +30 -6
  184. data/lib/capybara/spec/session/has_none_selectors_spec.rb +26 -24
  185. data/lib/capybara/spec/session/has_select_spec.rb +75 -47
  186. data/lib/capybara/spec/session/has_selector_spec.rb +102 -69
  187. data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
  188. data/lib/capybara/spec/session/has_table_spec.rb +170 -4
  189. data/lib/capybara/spec/session/has_text_spec.rb +108 -52
  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 +35 -0
  195. data/lib/capybara/spec/session/node_spec.rb +871 -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 +52 -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 +117 -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 +40 -21
  212. data/lib/capybara/spec/session/unselect_spec.rb +37 -37
  213. data/lib/capybara/spec/session/visit_spec.rb +59 -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 +10 -7
  221. data/lib/capybara/spec/session/window/within_window_spec.rb +17 -16
  222. data/lib/capybara/spec/session/within_spec.rb +69 -44
  223. data/lib/capybara/spec/spec_helper.rb +41 -53
  224. data/lib/capybara/spec/test_app.rb +79 -40
  225. data/lib/capybara/spec/views/animated.erb +49 -0
  226. data/lib/capybara/spec/views/form.erb +134 -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 +43 -10
  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_other.erb +6 -0
  255. data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
  256. data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
  257. data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
  258. data/lib/capybara/spec/views/with_windows.erb +1 -1
  259. data/lib/capybara/spec/views/within_frames.erb +1 -1
  260. data/lib/capybara/version.rb +1 -1
  261. data/lib/capybara/window.rb +31 -25
  262. data/lib/capybara.rb +120 -100
  263. data/spec/basic_node_spec.rb +59 -34
  264. data/spec/capybara_spec.rb +54 -52
  265. data/spec/css_builder_spec.rb +101 -0
  266. data/spec/css_splitter_spec.rb +38 -0
  267. data/spec/dsl_spec.rb +80 -53
  268. data/spec/filter_set_spec.rb +24 -7
  269. data/spec/fixtures/certificate.pem +25 -0
  270. data/spec/fixtures/key.pem +27 -0
  271. data/spec/fixtures/selenium_driver_rspec_failure.rb +4 -4
  272. data/spec/fixtures/selenium_driver_rspec_success.rb +4 -4
  273. data/spec/minitest_spec.rb +38 -5
  274. data/spec/minitest_spec_spec.rb +88 -62
  275. data/spec/per_session_config_spec.rb +5 -5
  276. data/spec/rack_test_spec.rb +169 -115
  277. data/spec/regexp_dissassembler_spec.rb +250 -0
  278. data/spec/result_spec.rb +86 -33
  279. data/spec/rspec/features_spec.rb +26 -23
  280. data/spec/rspec/scenarios_spec.rb +8 -4
  281. data/spec/rspec/shared_spec_matchers.rb +393 -363
  282. data/spec/rspec/views_spec.rb +4 -3
  283. data/spec/rspec_matchers_spec.rb +10 -10
  284. data/spec/rspec_spec.rb +109 -85
  285. data/spec/sauce_spec_chrome.rb +43 -0
  286. data/spec/selector_spec.rb +391 -61
  287. data/spec/selenium_spec_chrome.rb +180 -41
  288. data/spec/selenium_spec_chrome_remote.rb +101 -0
  289. data/spec/selenium_spec_edge.rb +28 -8
  290. data/spec/selenium_spec_firefox.rb +211 -0
  291. data/spec/selenium_spec_firefox_remote.rb +80 -0
  292. data/spec/selenium_spec_ie.rb +127 -11
  293. data/spec/selenium_spec_safari.rb +156 -0
  294. data/spec/server_spec.rb +177 -78
  295. data/spec/session_spec.rb +52 -16
  296. data/spec/shared_selenium_node.rb +79 -0
  297. data/spec/shared_selenium_session.rb +455 -123
  298. data/spec/spec_helper.rb +91 -2
  299. data/spec/xpath_builder_spec.rb +93 -0
  300. metadata +343 -42
  301. data/.yard/templates_custom/default/class/html/selectors.erb +0 -38
  302. data/.yard/templates_custom/default/class/html/setup.rb +0 -17
  303. data/.yard/yard_extensions.rb +0 -78
  304. data/lib/capybara/rspec/compound.rb +0 -90
  305. data/lib/capybara/spec/session/assert_selector.rb +0 -149
  306. data/lib/capybara/spec/session/source_spec.rb +0 -0
  307. data/lib/capybara/spec/views/with_title.erb +0 -5
  308. data/spec/selenium_spec_marionette.rb +0 -143
@@ -1,554 +1,243 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'capybara/selector/xpath_extensions'
3
4
  require 'capybara/selector/selector'
4
- Capybara::Selector::FilterSet.add(:_field) do
5
- filter(:checked, :boolean) { |node, value| !(value ^ node.checked?) }
6
- filter(:unchecked, :boolean) { |node, value| (value ^ node.checked?) }
7
- filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| !(value ^ node.disabled?) }
8
- filter(:multiple, :boolean) { |node, value| !(value ^ node.multiple?) }
9
-
10
- expression_filter(:name) { |xpath, val| xpath[XPath.attr(:name) == val] }
11
- expression_filter(:placeholder) { |xpath, val| xpath[XPath.attr(:placeholder) == val] }
12
-
13
- describe do |checked: nil, unchecked: nil, disabled: nil, multiple: nil, **_options|
14
- desc, states = "".dup, []
15
- states << 'checked' if checked || (unchecked == false)
16
- states << 'not checked' if unchecked || (checked == false)
17
- states << 'disabled' if disabled == true
18
- states << 'not disabled' if disabled == false
19
- desc << " that is #{states.join(' and ')}" unless states.empty?
20
- desc << " with the multiple attribute" if multiple == true
21
- desc << " without the multiple attribute" if multiple == false
22
- desc
23
- end
24
- end
25
-
26
- # rubocop:disable Metrics/BlockLength
27
- # rubocop:disable Metrics/ParameterLists
28
-
29
- ##
30
- #
31
- # Select elements by XPath expression
32
- #
33
- # @locator An XPath expression
34
- #
35
- Capybara.add_selector(:xpath) do
36
- xpath { |xpath| xpath }
37
- end
38
-
39
- ##
40
- #
41
- # Select elements by CSS selector
42
- #
43
- # @locator A CSS selector
44
- #
45
- Capybara.add_selector(:css) do
46
- css { |css| css }
47
- end
48
-
49
- ##
50
- #
51
- # Select element by id
52
- #
53
- # @locator The id of the element to match
54
- #
55
- Capybara.add_selector(:id) do
56
- xpath { |id| XPath.descendant[XPath.attr(:id) == id.to_s] }
57
- end
58
-
59
- ##
60
- #
61
- # Select field elements (input [not of type submit, image, or hidden], textarea, select)
62
- #
63
- # @locator Matches against the id, name, or placeholder
64
- # @filter [String] :id Matches the id attribute
65
- # @filter [String] :name Matches the name attribute
66
- # @filter [String] :placeholder Matches the placeholder attribute
67
- # @filter [String] :type Matches the type attribute of the field or element type for 'textarea' and 'select'
68
- # @filter [Boolean] :readonly
69
- # @filter [String] :with Matches the current value of the field
70
- # @filter [String, Array<String>] :class Matches the class(es) provided
71
- # @filter [Boolean] :checked Match checked fields?
72
- # @filter [Boolean] :unchecked Match unchecked fields?
73
- # @filter [Boolean] :disabled Match disabled field?
74
- # @filter [Boolean] :multiple Match fields that accept multiple values
75
- Capybara.add_selector(:field) do
76
- xpath do |locator, **options|
77
- xpath = XPath.descendant(:input, :textarea, :select)[!XPath.attr(:type).one_of('submit', 'image', 'hidden')]
78
- locate_field(xpath, locator, options)
79
- end
80
-
81
- expression_filter(:type) do |expr, type|
82
- type = type.to_s
83
- if %w[textarea select].include?(type)
84
- expr.self(type.to_sym)
85
- else
86
- expr[XPath.attr(:type) == type]
87
- end
88
- end
89
-
90
- filter_set(:_field) # checked/unchecked/disabled/multiple/name/placeholder
91
-
92
- filter(:readonly, :boolean) { |node, value| !(value ^ node.readonly?) }
93
- filter(:with) do |node, with|
94
- with.is_a?(Regexp) ? node.value =~ with : node.value == with.to_s
95
- end
96
- describe do |type: nil, **options|
97
- desc = "".dup
98
- (expression_filters.keys - [:type]).each { |ef| desc << " with #{ef} #{options[ef]}" if options.key?(ef) }
99
- desc << " of type #{type.inspect}" if type
100
- desc << " with value #{options[:with].to_s.inspect}" if options.key?(:with)
101
- desc
102
- end
103
- end
104
-
105
- ##
106
- #
107
- # Select fieldset elements
108
- #
109
- # @locator Matches id or contents of wrapped legend
110
- #
111
- # @filter [String] :id Matches id attribute
112
- # @filter [String] :legend Matches contents of wrapped legend
113
- # @filter [String, Array<String>] :class Matches the class(es) provided
114
- #
115
- Capybara.add_selector(:fieldset) do
116
- xpath(:legend) do |locator, legend: nil, **_options|
117
- xpath = XPath.descendant(:fieldset)
118
- xpath = xpath[(XPath.attr(:id) == locator.to_s) | XPath.child(:legend)[XPath.string.n.is(locator.to_s)]] unless locator.nil?
119
- xpath = xpath[XPath.child(:legend)[XPath.string.n.is(legend)]] if legend
120
- xpath
121
- end
122
- end
123
-
124
- ##
125
- #
126
- # Find links ( <a> elements with an href attribute )
127
- #
128
- # @locator Matches the id or title attributes, or the string content of the link, or the alt attribute of a contained img element
129
- #
130
- # @filter [String] :id Matches the id attribute
131
- # @filter [String] :title Matches the title attribute
132
- # @filter [String] :alt Matches the alt attribute of a contained img element
133
- # @filter [String] :class Matches the class(es) provided
134
- # @filter [String, Regexp,nil] :href Matches the normalized href of the link, if nil will find <a> elements with no href attribute
135
- #
136
- Capybara.add_selector(:link) do
137
- xpath(:title, :alt) do |locator, href: true, enable_aria_label: false, alt: nil, title: nil, **_options|
138
- xpath = XPath.descendant(:a)
139
- xpath = if href.nil?
140
- xpath[!XPath.attr(:href)]
141
- else
142
- xpath[XPath.attr(:href)]
143
- end
144
- unless locator.nil?
145
- locator = locator.to_s
146
- matchers = [XPath.attr(:id) == locator,
147
- XPath.string.n.is(locator),
148
- XPath.attr(:title).is(locator),
149
- XPath.descendant(:img)[XPath.attr(:alt).is(locator)]].reduce(:|)
150
- matchers |= XPath.attr(:'aria-label').is(locator) if enable_aria_label
151
- xpath = xpath[matchers]
152
- end
153
- xpath = xpath[find_by_attr(:title, title)]
154
- xpath = xpath[XPath.descendant(:img)[XPath.attr(:alt) == alt]] if alt
155
- xpath
156
- end
157
-
158
- filter(:href) do |node, href|
159
- case href
160
- when nil
161
- true
162
- when Regexp
163
- node[:href].match href
164
- else
165
- node.first(:xpath, XPath.self[XPath.attr(:href) == href.to_s], minimum: 0)
166
- end
167
- end
168
-
169
- describe do |**options|
170
- desc = "".dup
171
- desc << " with href #{options[:href].inspect}" if options[:href]
172
- desc << " with no href attribute" if options.fetch(:href, true).nil?
173
- end
174
- end
175
-
176
- ##
177
- #
178
- # Find buttons ( input [of type submit, reset, image, button] or button elements )
179
- #
180
- # @locator Matches the id, value, or title attributes, string content of a button, or the alt attribute of an image type button or of a descendant image of a button
181
- #
182
- # @filter [String] :id Matches the id attribute
183
- # @filter [String] :title Matches the title attribute
184
- # @filter [String] :class Matches the class(es) provided
185
- # @filter [String] :value Matches the value of an input button
186
- #
187
- Capybara.add_selector(:button) do
188
- xpath(:value, :title) do |locator, **options|
189
- input_btn_xpath = XPath.descendant(:input)[XPath.attr(:type).one_of('submit', 'reset', 'image', 'button')]
190
- btn_xpath = XPath.descendant(:button)
191
- image_btn_xpath = XPath.descendant(:input)[XPath.attr(:type) == 'image']
192
-
193
- unless locator.nil?
194
- locator = locator.to_s
195
- locator_matches = XPath.attr(:id).equals(locator) | XPath.attr(:value).is(locator) | XPath.attr(:title).is(locator)
196
- locator_matches |= XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
197
-
198
- input_btn_xpath = input_btn_xpath[locator_matches]
199
-
200
- btn_xpath = btn_xpath[locator_matches | XPath.string.n.is(locator) | XPath.descendant(:img)[XPath.attr(:alt).is(locator)]]
201
-
202
- alt_matches = XPath.attr(:alt).is(locator)
203
- alt_matches |= XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
204
- image_btn_xpath = image_btn_xpath[alt_matches]
205
- end
206
-
207
- res_xpath = input_btn_xpath.union(btn_xpath).union(image_btn_xpath)
208
-
209
- res_xpath = expression_filters.keys.inject(res_xpath) { |memo, ef| memo[find_by_attr(ef, options[ef])] }
210
-
211
- res_xpath
212
- end
213
-
214
- filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| !(value ^ node.disabled?) }
215
-
216
- describe do |disabled: nil, **options|
217
- desc = "".dup
218
- desc << " that is disabled" if disabled == true
219
- desc << describe_all_expression_filters(options)
220
- desc
221
- end
222
- end
223
-
224
- ##
225
- #
226
- # Find links or buttons
227
- #
228
- Capybara.add_selector(:link_or_button) do
229
- label "link or button"
230
- xpath do |locator, **options|
231
- self.class.all.values_at(:link, :button).map { |selector| selector.xpath.call(locator, options) }.reduce(:union)
232
- end
233
-
234
- filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| node.tag_name == "a" or !(value ^ node.disabled?) }
235
-
236
- describe { |disabled: nil, **_options| " that is disabled" if disabled == true }
237
- end
238
-
239
- ##
240
- #
241
- # Find text fillable fields ( textarea, input [not of type submit, image, radio, checkbox, hidden, file] )
242
- #
243
- # @locator Matches against the id, name, or placeholder
244
- # @filter [String] :id Matches the id attribute
245
- # @filter [String] :name Matches the name attribute
246
- # @filter [String] :placeholder Matches the placeholder attribute
247
- # @filter [String] :with Matches the current value of the field
248
- # @filter [String] :type Matches the type attribute of the field or element type for 'textarea'
249
- # @filter [String, Array<String>] :class Matches the class(es) provided
250
- # @filter [Boolean] :disabled Match disabled field?
251
- # @filter [Boolean] :multiple Match fields that accept multiple values
252
- #
253
- Capybara.add_selector(:fillable_field) do
254
- label "field"
255
-
256
- xpath do |locator, **options|
257
- xpath = XPath.descendant(:input, :textarea)[!XPath.attr(:type).one_of('submit', 'image', 'radio', 'checkbox', 'hidden', 'file')]
258
- locate_field(xpath, locator, options)
259
- end
260
-
261
- expression_filter(:type) do |expr, type|
262
- type = type.to_s
263
- if ['textarea'].include?(type)
264
- expr.self(type.to_sym)
265
- else
266
- expr[XPath.attr(:type) == type]
267
- end
268
- end
5
+ require 'capybara/selector/definition'
6
+
7
+ #
8
+ # All Selectors below support the listed selector specific filters in addition to the following system-wide filters
9
+ # * :id (String, Regexp, XPath::Expression) - Matches the id attribute
10
+ # * :class (String, Array<String | Regexp>, Regexp, XPath::Expression) - Matches the class(es) provided
11
+ # * :style (String, Regexp, Hash<String, String>) - Match on elements style
12
+ # * :above (Element) - Match elements above the passed element on the page
13
+ # * :below (Element) - Match elements below the passed element on the page
14
+ # * :left_of (Element) - Match elements left of the passed element on the page
15
+ # * :right_of (Element) - Match elements right of the passed element on the page
16
+ # * :near (Element) - Match elements near (within 50px) the passed element on the page
17
+ # * :focused (Boolean) - Match elements with focus (requires driver support)
18
+ #
19
+ # ### Built-in Selectors
20
+ #
21
+ # * **:xpath** - Select elements by XPath expression
22
+ # * Locator: An XPath expression
23
+ #
24
+ # * **:css** - Select elements by CSS selector
25
+ # * Locator: A CSS selector
26
+ #
27
+ # * **:id** - Select element by id
28
+ # * Locator: (String, Regexp, XPath::Expression) The id of the element to match
29
+ #
30
+ # * **:field** - Select field elements (input [not of type submit, image, or hidden], textarea, select)
31
+ # * Locator: Matches against the id, {Capybara.configure test_id} attribute, name, placeholder, or
32
+ # associated label text
33
+ # * Filters:
34
+ # * :name (String, Regexp) - Matches the name attribute
35
+ # * :placeholder (String, Regexp) - Matches the placeholder attribute
36
+ # * :type (String) - Matches the type attribute of the field or element type for 'textarea' and 'select'
37
+ # * :readonly (Boolean) - Match on the element being readonly
38
+ # * :with (String, Regexp) - Matches the current value of the field
39
+ # * :checked (Boolean) - Match checked fields?
40
+ # * :unchecked (Boolean) - Match unchecked fields?
41
+ # * :disabled (Boolean, :all) - Match disabled field? (Default: false)
42
+ # * :multiple (Boolean) - Match fields that accept multiple values
43
+ # * :valid (Boolean) - Match fields that are valid/invalid according to HTML5 form validation
44
+ # * :validation_message (String, Regexp) - Matches the elements current validationMessage
45
+ #
46
+ # * **:fieldset** - Select fieldset elements
47
+ # * Locator: Matches id, {Capybara.configure test_id}, or contents of wrapped legend
48
+ # * Filters:
49
+ # * :legend (String) - Matches contents of wrapped legend
50
+ # * :disabled (Boolean) - Match disabled fieldset?
51
+ #
52
+ # * **:link** - Find links (`<a>` elements with an href attribute)
53
+ # * Locator: Matches the id, {Capybara.configure test_id}, or title attributes, or the string content of the link,
54
+ # or the alt attribute of a contained img element. By default this selector requires a link to have an href attribute.
55
+ # * Filters:
56
+ # * :title (String) - Matches the title attribute
57
+ # * :alt (String) - Matches the alt attribute of a contained img element
58
+ # * :href (String, Regexp, nil, false) - Matches the normalized href of the link, if nil will find `<a>` elements with no href attribute, if false ignores href presence
59
+ #
60
+ # * **:button** - Find buttons ( input [of type submit, reset, image, button] or button elements )
61
+ # * Locator: Matches the id, {Capybara.configure test_id} attribute, name, value, or title attributes, string content of a button, or the alt attribute of an image type button or of a descendant image of a button
62
+ # * Filters:
63
+ # * :name (String, Regexp) - Matches the name attribute
64
+ # * :title (String) - Matches the title attribute
65
+ # * :value (String) - Matches the value of an input button
66
+ # * :type (String) - Matches the type attribute
67
+ # * :disabled (Boolean, :all) - Match disabled buttons (Default: false)
68
+ #
69
+ # * **:link_or_button** - Find links or buttons
70
+ # * Locator: See :link and :button selectors
71
+ # * Filters:
72
+ # * :disabled (Boolean, :all) - Match disabled buttons? (Default: false)
73
+ #
74
+ # * **:fillable_field** - Find text fillable fields ( textarea, input [not of type submit, image, radio, checkbox, hidden, file] )
75
+ # * Locator: Matches against the id, {Capybara.configure test_id} attribute, name, placeholder, or associated label text
76
+ # * Filters:
77
+ # * :name (String, Regexp) - Matches the name attribute
78
+ # * :placeholder (String, Regexp) - Matches the placeholder attribute
79
+ # * :with (String, Regexp) - Matches the current value of the field
80
+ # * :type (String) - Matches the type attribute of the field or element type for 'textarea'
81
+ # * :disabled (Boolean, :all) - Match disabled field? (Default: false)
82
+ # * :multiple (Boolean) - Match fields that accept multiple values
83
+ # * :valid (Boolean) - Match fields that are valid/invalid according to HTML5 form validation
84
+ # * :validation_message (String, Regexp) - Matches the elements current validationMessage
85
+ #
86
+ # * **:radio_button** - Find radio buttons
87
+ # * Locator: Match id, {Capybara.configure test_id} attribute, name, or associated label text
88
+ # * Filters:
89
+ # * :name (String, Regexp) - Matches the name attribute
90
+ # * :checked (Boolean) - Match checked fields?
91
+ # * :unchecked (Boolean) - Match unchecked fields?
92
+ # * :disabled (Boolean, :all) - Match disabled field? (Default: false)
93
+ # * :option (String, Regexp) - Match the current value
94
+ # * :with - Alias of :option
95
+ #
96
+ # * **:checkbox** - Find checkboxes
97
+ # * Locator: Match id, {Capybara.configure test_id} attribute, name, or associated label text
98
+ # * Filters:
99
+ # * :name (String, Regexp) - Matches the name attribute
100
+ # * :checked (Boolean) - Match checked fields?
101
+ # * :unchecked (Boolean) - Match unchecked fields?
102
+ # * :disabled (Boolean, :all) - Match disabled field? (Default: false)
103
+ # * :with (String, Regexp) - Match the current value
104
+ # * :option - Alias of :with
105
+ #
106
+ # * **:select** - Find select elements
107
+ # * Locator: Match id, {Capybara.configure test_id} attribute, name, placeholder, or associated label text
108
+ # * Filters:
109
+ # * :name (String, Regexp) - Matches the name attribute
110
+ # * :placeholder (String, Placeholder) - Matches the placeholder attribute
111
+ # * :disabled (Boolean, :all) - Match disabled field? (Default: false)
112
+ # * :multiple (Boolean) - Match fields that accept multiple values
113
+ # * :options (Array<String>) - Exact match options
114
+ # * :enabled_options (Array<String>) - Exact match enabled options
115
+ # * :disabled_options (Array<String>) - Exact match disabled options
116
+ # * :with_options (Array<String>) - Partial match options
117
+ # * :selected (String, Array<String>) - Match the selection(s)
118
+ # * :with_selected (String, Array<String>) - Partial match the selection(s)
119
+ #
120
+ # * **:option** - Find option elements
121
+ # * Locator: Match text of option
122
+ # * Filters:
123
+ # * :disabled (Boolean) - Match disabled option
124
+ # * :selected (Boolean) - Match selected option
125
+ #
126
+ # * **:datalist_input** - Find input field with datalist completion
127
+ # * Locator: Matches against the id, {Capybara.configure test_id} attribute, name,
128
+ # placeholder, or associated label text
129
+ # * Filters:
130
+ # * :name (String, Regexp) - Matches the name attribute
131
+ # * :placeholder (String, Regexp) - Matches the placeholder attribute
132
+ # * :disabled (Boolean, :all) - Match disabled field? (Default: false)
133
+ # * :options (Array<String>) - Exact match options
134
+ # * :with_options (Array<String>) - Partial match options
135
+ #
136
+ # * **:datalist_option** - Find datalist option
137
+ # * Locator: Match text or value of option
138
+ # * Filters:
139
+ # * :disabled (Boolean) - Match disabled option
140
+ #
141
+ # * **:file_field** - Find file input elements
142
+ # * Locator: Match id, {Capybara.configure test_id} attribute, name, or associated label text
143
+ # * Filters:
144
+ # * :name (String, Regexp) - Matches the name attribute
145
+ # * :disabled (Boolean, :all) - Match disabled field? (Default: false)
146
+ # * :multiple (Boolean) - Match field that accepts multiple values
147
+ #
148
+ # * **:label** - Find label elements
149
+ # * Locator: Match id, {Capybara.configure test_id}, or text contents
150
+ # * Filters:
151
+ # * :for (Element, String, Regexp) - The element or id of the element associated with the label
152
+ #
153
+ # * **:table** - Find table elements
154
+ # * Locator: id, {Capybara.configure test_id}, or caption text of table
155
+ # * Filters:
156
+ # * :caption (String) - Match text of associated caption
157
+ # * :with_rows (Array<Array<String>>, Array<Hash<String, String>>) - Partial match `<td>` data - visibility of `<td>` elements is not considered
158
+ # * :rows (Array<Array<String>>) - Match all `<td>`s - visibility of `<td>` elements is not considered
159
+ # * :with_cols (Array<Array<String>>, Array<Hash<String, String>>) - Partial match `<td>` data - visibility of `<td>` elements is not considered
160
+ # * :cols (Array<Array<String>>) - Match all `<td>`s - visibility of `<td>` elements is not considered
161
+ #
162
+ # * **:table_row** - Find table row
163
+ # * Locator: Array<String>, Hash<String, String> table row `<td>` contents - visibility of `<td>` elements is not considered
164
+ #
165
+ # * **:frame** - Find frame/iframe elements
166
+ # * Locator: Match id, {Capybara.configure test_id} attribute, or name
167
+ # * Filters:
168
+ # * :name (String) - Match name attribute
169
+ #
170
+ # * **:element**
171
+ # * Locator: Type of element ('div', 'a', etc) - if not specified defaults to '*'
172
+ # * Filters:
173
+ # * :\<any> (String, Regexp) - Match on any specified element attribute
174
+ #
175
+ class Capybara::Selector; end # rubocop:disable Lint/EmptyClass
269
176
 
270
- filter_set(:_field, %i[disabled multiple name placeholder])
271
-
272
- filter(:with) do |node, with|
273
- with.is_a?(Regexp) ? node.value =~ with : node.value == with.to_s
274
- end
275
-
276
- describe do |options|
277
- desc = "".dup
278
- desc << describe_all_expression_filters(options)
279
- desc << " with value #{options[:with].to_s.inspect}" if options.key?(:with)
280
- desc
281
- end
282
- end
283
-
284
- ##
285
- #
286
- # Find radio buttons
287
- #
288
- # @locator Match id, name, or associated label text
289
- # @filter [String] :id Matches the id attribute
290
- # @filter [String] :name Matches the name attribute
291
- # @filter [String, Array<String>] :class Matches the class(es) provided
292
- # @filter [Boolean] :checked Match checked fields?
293
- # @filter [Boolean] :unchecked Match unchecked fields?
294
- # @filter [Boolean] :disabled Match disabled field?
295
- # @filter [String] :option Match the value
296
- #
297
- Capybara.add_selector(:radio_button) do
298
- label "radio button"
299
-
300
- xpath do |locator, **options|
301
- xpath = XPath.descendant(:input)[XPath.attr(:type) == 'radio']
302
- locate_field(xpath, locator, options)
303
- end
304
-
305
- filter_set(:_field, %i[checked unchecked disabled name])
306
-
307
- filter(:option) { |node, value| node.value == value.to_s }
308
-
309
- describe do |option: nil, **options|
310
- desc = "".dup
311
- desc << " with value #{option.inspect}" if option
312
- desc << describe_all_expression_filters(options)
313
- desc
314
- end
315
- end
316
-
317
- ##
318
- #
319
- # Find checkboxes
320
- #
321
- # @locator Match id, name, or associated label text
322
- # @filter [String] :id Matches the id attribute
323
- # @filter [String] :name Matches the name attribute
324
- # @filter [String, Array<String>] :class Matches the class(es) provided
325
- # @filter [Boolean] :checked Match checked fields?
326
- # @filter [Boolean] :unchecked Match unchecked fields?
327
- # @filter [Boolean] :disabled Match disabled field?
328
- # @filter [String] :option Match the value
329
- #
330
- Capybara.add_selector(:checkbox) do
331
- xpath do |locator, **options|
332
- xpath = XPath.descendant(:input)[XPath.attr(:type) == 'checkbox']
333
- locate_field(xpath, locator, options)
334
- end
335
-
336
- filter_set(:_field, %i[checked unchecked disabled name])
337
-
338
- filter(:option) { |node, value| node.value == value.to_s }
339
-
340
- describe do |option: nil, **options|
341
- desc = "".dup
342
- desc << " with value #{option.inspect}" if option
343
- desc << describe_all_expression_filters(options)
344
- desc
345
- end
346
- end
347
-
348
- ##
349
- #
350
- # Find select elements
351
- #
352
- # @locator Match id, name, placeholder, or associated label text
353
- # @filter [String] :id Matches the id attribute
354
- # @filter [String] :name Matches the name attribute
355
- # @filter [String] :placeholder Matches the placeholder attribute
356
- # @filter [String, Array<String>] :class Matches the class(es) provided
357
- # @filter [Boolean] :disabled Match disabled field?
358
- # @filter [Boolean] :multiple Match fields that accept multiple values
359
- # @filter [Array<String>] :options Exact match options
360
- # @filter [Array<String>] :with_options Partial match options
361
- # @filter [String, Array<String>] :selected Match the selection(s)
362
- # @filter [String, Array<String>] :with_selected Partial match the selection(s)
363
- #
364
- Capybara.add_selector(:select) do
365
- label "select box"
366
-
367
- xpath do |locator, **options|
368
- xpath = XPath.descendant(:select)
369
- locate_field(xpath, locator, options)
370
- end
371
-
372
- filter_set(:_field, %i[disabled multiple name placeholder])
373
-
374
- filter(:options) do |node, options|
375
- actual = if node.visible?
376
- node.all(:xpath, './/option', wait: false).map(&:text)
377
- else
378
- node.all(:xpath, './/option', visible: false, wait: false).map { |option| option.text(:all) }
379
- end
380
- options.sort == actual.sort
381
- end
382
-
383
- filter(:with_options) do |node, options|
384
- finder_settings = { minimum: 0 }
385
- finder_settings[:visible] = false unless node.visible?
386
- options.all? { |option| node.first(:option, option, finder_settings) }
387
- end
388
-
389
- filter(:selected) do |node, selected|
390
- actual = node.all(:xpath, './/option', visible: false, wait: false).select(&:selected?).map { |option| option.text(:all) }
391
- Array(selected).sort == actual.sort
392
- end
393
-
394
- filter(:with_selected) do |node, selected|
395
- actual = node.all(:xpath, './/option', visible: false, wait: false).select(&:selected?).map { |option| option.text(:all) }
396
- (Array(selected) - actual).empty?
397
- end
398
-
399
- describe do |options: nil, with_options: nil, selected: nil, with_selected: nil, **opts|
400
- desc = "".dup
401
- desc << " with options #{options.inspect}" if options
402
- desc << " with at least options #{with_options.inspect}" if with_options
403
- desc << " with #{selected.inspect} selected" if selected
404
- desc << " with at least #{with_selected.inspect} selected" if with_selected
405
- desc << describe_all_expression_filters(opts)
406
- desc
407
- end
408
- end
409
-
410
- ##
411
- #
412
- # Find option elements
413
- #
414
- # @locator Match text of option
415
- # @filter [Boolean] :disabled Match disabled option
416
- # @filter [Boolean] :selected Match selected option
417
- #
418
- Capybara.add_selector(:option) do
419
- xpath do |locator|
420
- xpath = XPath.descendant(:option)
421
- xpath = xpath[XPath.string.n.is(locator.to_s)] unless locator.nil?
422
- xpath
423
- end
424
-
425
- filter(:disabled, :boolean) { |node, value| !(value ^ node.disabled?) }
426
- filter(:selected, :boolean) { |node, value| !(value ^ node.selected?) }
427
-
428
- describe do |**options|
429
- desc = "".dup
430
- desc << " that is#{' not' unless options[:disabled]} disabled" if options.key?(:disabled)
431
- desc << " that is#{' not' unless options[:selected]} selected" if options.key?(:selected)
432
- desc
433
- end
434
- end
435
-
436
- ##
437
- #
438
- # Find file input elements
439
- #
440
- # @locator Match id, name, or associated label text
441
- # @filter [String] :id Matches the id attribute
442
- # @filter [String] :name Matches the name attribute
443
- # @filter [String, Array<String>] :class Matches the class(es) provided
444
- # @filter [Boolean] :disabled Match disabled field?
445
- # @filter [Boolean] :multiple Match field that accepts multiple values
446
- #
447
- Capybara.add_selector(:file_field) do
448
- label "file field"
449
- xpath do |locator, options|
450
- xpath = XPath.descendant(:input)[XPath.attr(:type) == 'file']
451
- locate_field(xpath, locator, options)
452
- end
453
-
454
- filter_set(:_field, %i[disabled multiple name])
455
-
456
- describe do |**options|
457
- desc = "".dup
458
- desc << describe_all_expression_filters(options)
459
- desc
460
- end
461
- end
462
-
463
- ##
464
- #
465
- # Find label elements
466
- #
467
- # @locator Match id or text contents
468
- # @filter [Element, String] :for The element or id of the element associated with the label
469
- #
470
- Capybara.add_selector(:label) do
471
- label "label"
472
- xpath(:for) do |locator, options|
473
- xpath = XPath.descendant(:label)
474
- xpath = xpath[XPath.string.n.is(locator.to_s) | (XPath.attr(:id) == locator.to_s)] unless locator.nil?
475
- if options.key?(:for) && !options[:for].is_a?(Capybara::Node::Element)
476
- with_attr = XPath.attr(:for) == options[:for].to_s
477
- labelable_elements = %i[button input keygen meter output progress select textarea]
478
- wrapped = !XPath.attr(:for) &
479
- XPath.descendant(*labelable_elements)[XPath.attr(:id) == options[:for].to_s]
480
- xpath = xpath[with_attr | wrapped]
481
- end
482
- xpath
483
- end
484
-
485
- filter(:for) do |node, field_or_value|
486
- if field_or_value.is_a? Capybara::Node::Element
487
- if node[:for]
488
- field_or_value[:id] == node[:for]
489
- else
490
- field_or_value.find_xpath('./ancestor::label[1]').include? node.base
491
- end
492
- else
493
- # Non element values were handled through the expression filter
494
- true
177
+ Capybara::Selector::FilterSet.add(:_field) do
178
+ node_filter(:checked, :boolean) { |node, value| !(value ^ node.checked?) }
179
+ node_filter(:unchecked, :boolean) { |node, value| (value ^ node.checked?) }
180
+ node_filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| !(value ^ node.disabled?) }
181
+ node_filter(:valid, :boolean) { |node, value| node.evaluate_script('this.validity.valid') == value }
182
+ node_filter(:name) { |node, value| !value.is_a?(Regexp) || value.match?(node[:name]) }
183
+ node_filter(:placeholder) { |node, value| !value.is_a?(Regexp) || value.match?(node[:placeholder]) }
184
+ node_filter(:validation_message) do |node, msg|
185
+ vm = node[:validationMessage]
186
+ (msg.is_a?(Regexp) ? msg.match?(vm) : vm == msg.to_s).tap do |res|
187
+ add_error("Expected validation message to be #{msg.inspect} but was #{vm}") unless res
495
188
  end
496
189
  end
497
190
 
498
- describe do |**options|
499
- desc = "".dup
500
- desc << " for #{options[:for]}" if options[:for]
501
- desc
191
+ expression_filter(:name) do |xpath, val|
192
+ builder(xpath).add_attribute_conditions(name: val)
502
193
  end
503
- end
504
-
505
- ##
506
- #
507
- # Find table elements
508
- #
509
- # @locator id or caption text of table
510
- # @filter [String] :id Match id attribute of table
511
- # @filter [String] :caption Match text of associated caption
512
- # @filter [String, Array<String>] :class Matches the class(es) provided
513
- #
514
- Capybara.add_selector(:table) do
515
- xpath(:caption) do |locator, options|
516
- xpath = XPath.descendant(:table)
517
- xpath = xpath[(XPath.attr(:id) == locator.to_s) | XPath.descendant(:caption).is(locator.to_s)] unless locator.nil?
518
- xpath = xpath[XPath.descendant(:caption) == options[:caption]] if options[:caption]
519
- xpath
194
+ expression_filter(:placeholder) do |xpath, val|
195
+ builder(xpath).add_attribute_conditions(placeholder: val)
520
196
  end
197
+ expression_filter(:disabled) { |xpath, val| val ? xpath : xpath[~XPath.attr(:disabled)] }
198
+ expression_filter(:multiple) { |xpath, val| xpath[val ? XPath.attr(:multiple) : ~XPath.attr(:multiple)] }
521
199
 
522
- describe do |caption: nil, **_options|
523
- desc = "".dup
524
- desc << " with caption #{caption}" if caption
200
+ describe(:expression_filters) do |name: nil, placeholder: nil, disabled: nil, multiple: nil, **|
201
+ desc = +''
202
+ desc << ' that is not disabled' if disabled == false
203
+ desc << " with name #{name}" if name
204
+ desc << " with placeholder #{placeholder}" if placeholder
205
+ desc << ' with the multiple attribute' if multiple == true
206
+ desc << ' without the multiple attribute' if multiple == false
525
207
  desc
526
208
  end
527
- end
528
-
529
- ##
530
- #
531
- # Find frame/iframe elements
532
- #
533
- # @locator Match id or name
534
- # @filter [String] :id Match id attribute
535
- # @filter [String] :name Match name attribute
536
- # @filter [String, Array<String>] :class Matches the class(es) provided
537
- #
538
- Capybara.add_selector(:frame) do
539
- xpath(:name) do |locator, **options|
540
- xpath = XPath.descendant(:iframe).union(XPath.descendant(:frame))
541
- xpath = xpath[(XPath.attr(:id) == locator.to_s) | (XPath.attr(:name) == locator.to_s)] unless locator.nil?
542
- xpath = expression_filters.keys.inject(xpath) { |memo, ef| memo[find_by_attr(ef, options[ef])] }
543
- xpath
544
- end
545
209
 
546
- describe do |name: nil, **_options|
547
- desc = "".dup
548
- desc << " with name #{name}" if name
210
+ describe(:node_filters) do |checked: nil, unchecked: nil, disabled: nil, valid: nil, validation_message: nil, **|
211
+ desc, states = +'', []
212
+ states << 'checked' if checked || (unchecked == false)
213
+ states << 'not checked' if unchecked || (checked == false)
214
+ states << 'disabled' if disabled == true
215
+ desc << " that is #{states.join(' and ')}" unless states.empty?
216
+ desc << ' that is valid' if valid == true
217
+ desc << ' that is invalid' if valid == false
218
+ desc << " with validation message #{validation_message.to_s.inspect}" if validation_message
549
219
  desc
550
220
  end
551
221
  end
552
222
 
553
- # rubocop:enable Metrics/BlockLength
554
- # rubocop:enable Metrics/ParameterLists
223
+ require 'capybara/selector/definition/xpath'
224
+ require 'capybara/selector/definition/css'
225
+ require 'capybara/selector/definition/id'
226
+ require 'capybara/selector/definition/field'
227
+ require 'capybara/selector/definition/fieldset'
228
+ require 'capybara/selector/definition/link'
229
+ require 'capybara/selector/definition/button'
230
+ require 'capybara/selector/definition/link_or_button'
231
+ require 'capybara/selector/definition/fillable_field'
232
+ require 'capybara/selector/definition/radio_button'
233
+ require 'capybara/selector/definition/checkbox'
234
+ require 'capybara/selector/definition/select'
235
+ require 'capybara/selector/definition/datalist_input'
236
+ require 'capybara/selector/definition/option'
237
+ require 'capybara/selector/definition/datalist_option'
238
+ require 'capybara/selector/definition/file_field'
239
+ require 'capybara/selector/definition/label'
240
+ require 'capybara/selector/definition/table'
241
+ require 'capybara/selector/definition/table_row'
242
+ require 'capybara/selector/definition/frame'
243
+ require 'capybara/selector/definition/element'