capybara 3.0.0 → 3.36.0

Sign up to get free protection for your applications and to get access to all the features.
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'