capybara 3.0.0 → 3.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (312) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/History.md +891 -12
  4. data/License.txt +1 -1
  5. data/README.md +257 -84
  6. data/lib/capybara/config.rb +29 -10
  7. data/lib/capybara/cucumber.rb +1 -1
  8. data/lib/capybara/driver/base.rb +22 -4
  9. data/lib/capybara/driver/node.rb +38 -9
  10. data/lib/capybara/dsl.rb +9 -7
  11. data/lib/capybara/helpers.rb +57 -8
  12. data/lib/capybara/minitest/spec.rb +185 -84
  13. data/lib/capybara/minitest.rb +264 -145
  14. data/lib/capybara/node/actions.rb +248 -124
  15. data/lib/capybara/node/base.rb +35 -20
  16. data/lib/capybara/node/document.rb +14 -2
  17. data/lib/capybara/node/document_matchers.rb +13 -15
  18. data/lib/capybara/node/element.rb +350 -113
  19. data/lib/capybara/node/finders.rb +104 -82
  20. data/lib/capybara/node/matchers.rb +363 -157
  21. data/lib/capybara/node/simple.rb +54 -15
  22. data/lib/capybara/node/whitespace_normalizer.rb +81 -0
  23. data/lib/capybara/queries/active_element_query.rb +18 -0
  24. data/lib/capybara/queries/ancestor_query.rb +9 -10
  25. data/lib/capybara/queries/base_query.rb +25 -18
  26. data/lib/capybara/queries/current_path_query.rb +16 -6
  27. data/lib/capybara/queries/match_query.rb +11 -0
  28. data/lib/capybara/queries/selector_query.rb +617 -104
  29. data/lib/capybara/queries/sibling_query.rb +9 -7
  30. data/lib/capybara/queries/style_query.rb +45 -0
  31. data/lib/capybara/queries/text_query.rb +40 -22
  32. data/lib/capybara/queries/title_query.rb +2 -2
  33. data/lib/capybara/rack_test/browser.rb +106 -31
  34. data/lib/capybara/rack_test/driver.rb +16 -7
  35. data/lib/capybara/rack_test/errors.rb +6 -0
  36. data/lib/capybara/rack_test/form.rb +74 -49
  37. data/lib/capybara/rack_test/node.rb +120 -47
  38. data/lib/capybara/rails.rb +1 -1
  39. data/lib/capybara/registration_container.rb +41 -0
  40. data/lib/capybara/registrations/drivers.rb +42 -0
  41. data/lib/capybara/registrations/patches/puma_ssl.rb +29 -0
  42. data/lib/capybara/registrations/servers.rb +66 -0
  43. data/lib/capybara/result.rb +87 -53
  44. data/lib/capybara/rspec/features.rb +8 -10
  45. data/lib/capybara/rspec/matcher_proxies.rb +39 -18
  46. data/lib/capybara/rspec/matchers/base.rb +113 -0
  47. data/lib/capybara/rspec/matchers/become_closed.rb +33 -0
  48. data/lib/capybara/rspec/matchers/compound.rb +88 -0
  49. data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
  50. data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
  51. data/lib/capybara/rspec/matchers/have_current_path.rb +29 -0
  52. data/lib/capybara/rspec/matchers/have_selector.rb +69 -0
  53. data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
  54. data/lib/capybara/rspec/matchers/have_text.rb +33 -0
  55. data/lib/capybara/rspec/matchers/have_title.rb +29 -0
  56. data/lib/capybara/rspec/matchers/match_selector.rb +27 -0
  57. data/lib/capybara/rspec/matchers/match_style.rb +43 -0
  58. data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
  59. data/lib/capybara/rspec/matchers.rb +142 -315
  60. data/lib/capybara/rspec.rb +3 -2
  61. data/lib/capybara/selector/builders/css_builder.rb +84 -0
  62. data/lib/capybara/selector/builders/xpath_builder.rb +71 -0
  63. data/lib/capybara/selector/css.rb +85 -8
  64. data/lib/capybara/selector/definition/button.rb +68 -0
  65. data/lib/capybara/selector/definition/checkbox.rb +26 -0
  66. data/lib/capybara/selector/definition/css.rb +10 -0
  67. data/lib/capybara/selector/definition/datalist_input.rb +35 -0
  68. data/lib/capybara/selector/definition/datalist_option.rb +25 -0
  69. data/lib/capybara/selector/definition/element.rb +28 -0
  70. data/lib/capybara/selector/definition/field.rb +40 -0
  71. data/lib/capybara/selector/definition/fieldset.rb +14 -0
  72. data/lib/capybara/selector/definition/file_field.rb +13 -0
  73. data/lib/capybara/selector/definition/fillable_field.rb +33 -0
  74. data/lib/capybara/selector/definition/frame.rb +17 -0
  75. data/lib/capybara/selector/definition/id.rb +6 -0
  76. data/lib/capybara/selector/definition/label.rb +62 -0
  77. data/lib/capybara/selector/definition/link.rb +55 -0
  78. data/lib/capybara/selector/definition/link_or_button.rb +16 -0
  79. data/lib/capybara/selector/definition/option.rb +27 -0
  80. data/lib/capybara/selector/definition/radio_button.rb +27 -0
  81. data/lib/capybara/selector/definition/select.rb +81 -0
  82. data/lib/capybara/selector/definition/table.rb +109 -0
  83. data/lib/capybara/selector/definition/table_row.rb +21 -0
  84. data/lib/capybara/selector/definition/xpath.rb +5 -0
  85. data/lib/capybara/selector/definition.rb +280 -0
  86. data/lib/capybara/selector/filter.rb +1 -0
  87. data/lib/capybara/selector/filter_set.rb +72 -28
  88. data/lib/capybara/selector/filters/base.rb +45 -2
  89. data/lib/capybara/selector/filters/expression_filter.rb +5 -6
  90. data/lib/capybara/selector/filters/locator_filter.rb +29 -0
  91. data/lib/capybara/selector/filters/node_filter.rb +18 -4
  92. data/lib/capybara/selector/regexp_disassembler.rb +211 -0
  93. data/lib/capybara/selector/selector.rb +89 -200
  94. data/lib/capybara/selector/xpath_extensions.rb +17 -0
  95. data/lib/capybara/selector.rb +474 -534
  96. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -0
  97. data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -0
  98. data/lib/capybara/selenium/atoms/src/getAttribute.js +161 -0
  99. data/lib/capybara/selenium/atoms/src/isDisplayed.js +454 -0
  100. data/lib/capybara/selenium/driver.rb +270 -245
  101. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +117 -0
  102. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +128 -0
  103. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +84 -0
  104. data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +26 -0
  105. data/lib/capybara/selenium/driver_specializations/safari_driver.rb +24 -0
  106. data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
  107. data/lib/capybara/selenium/extensions/find.rb +110 -0
  108. data/lib/capybara/selenium/extensions/html5_drag.rb +229 -0
  109. data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
  110. data/lib/capybara/selenium/extensions/scroll.rb +76 -0
  111. data/lib/capybara/selenium/node.rb +460 -170
  112. data/lib/capybara/selenium/nodes/chrome_node.rb +125 -0
  113. data/lib/capybara/selenium/nodes/edge_node.rb +110 -0
  114. data/lib/capybara/selenium/nodes/firefox_node.rb +136 -0
  115. data/lib/capybara/selenium/nodes/ie_node.rb +22 -0
  116. data/lib/capybara/selenium/nodes/safari_node.rb +118 -0
  117. data/lib/capybara/selenium/patches/atoms.rb +18 -0
  118. data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
  119. data/lib/capybara/selenium/patches/logs.rb +45 -0
  120. data/lib/capybara/selenium/patches/pause_duration_fix.rb +9 -0
  121. data/lib/capybara/selenium/patches/persistent_client.rb +20 -0
  122. data/lib/capybara/server/animation_disabler.rb +80 -0
  123. data/lib/capybara/server/checker.rb +44 -0
  124. data/lib/capybara/server/middleware.rb +71 -0
  125. data/lib/capybara/server.rb +58 -67
  126. data/lib/capybara/session/config.rb +40 -6
  127. data/lib/capybara/session/matchers.rb +26 -19
  128. data/lib/capybara/session.rb +252 -194
  129. data/lib/capybara/spec/public/jquery.js +5 -5
  130. data/lib/capybara/spec/public/offset.js +6 -0
  131. data/lib/capybara/spec/public/test.js +126 -8
  132. data/lib/capybara/spec/session/accept_alert_spec.rb +11 -11
  133. data/lib/capybara/spec/session/accept_confirm_spec.rb +3 -3
  134. data/lib/capybara/spec/session/accept_prompt_spec.rb +9 -10
  135. data/lib/capybara/spec/session/active_element_spec.rb +31 -0
  136. data/lib/capybara/spec/session/all_spec.rb +135 -44
  137. data/lib/capybara/spec/session/ancestor_spec.rb +24 -19
  138. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +67 -38
  139. data/lib/capybara/spec/session/{assert_current_path.rb → assert_current_path_spec.rb} +20 -18
  140. data/lib/capybara/spec/session/assert_selector_spec.rb +143 -0
  141. data/lib/capybara/spec/session/assert_style_spec.rb +26 -0
  142. data/lib/capybara/spec/session/{assert_text.rb → assert_text_spec.rb} +76 -52
  143. data/lib/capybara/spec/session/{assert_title.rb → assert_title_spec.rb} +12 -12
  144. data/lib/capybara/spec/session/attach_file_spec.rb +126 -72
  145. data/lib/capybara/spec/session/body_spec.rb +11 -13
  146. data/lib/capybara/spec/session/check_spec.rb +112 -51
  147. data/lib/capybara/spec/session/choose_spec.rb +62 -30
  148. data/lib/capybara/spec/session/click_button_spec.rb +227 -161
  149. data/lib/capybara/spec/session/click_link_or_button_spec.rb +49 -30
  150. data/lib/capybara/spec/session/click_link_spec.rb +89 -55
  151. data/lib/capybara/spec/session/current_scope_spec.rb +8 -8
  152. data/lib/capybara/spec/session/current_url_spec.rb +44 -37
  153. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +3 -3
  154. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +2 -2
  155. data/lib/capybara/spec/session/element/{assert_match_selector.rb → assert_match_selector_spec.rb} +11 -9
  156. data/lib/capybara/spec/session/element/match_css_spec.rb +18 -10
  157. data/lib/capybara/spec/session/element/match_xpath_spec.rb +8 -6
  158. data/lib/capybara/spec/session/element/matches_selector_spec.rb +70 -56
  159. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +7 -7
  160. data/lib/capybara/spec/session/evaluate_script_spec.rb +28 -8
  161. data/lib/capybara/spec/session/execute_script_spec.rb +8 -7
  162. data/lib/capybara/spec/session/fill_in_spec.rb +110 -44
  163. data/lib/capybara/spec/session/find_button_spec.rb +23 -23
  164. data/lib/capybara/spec/session/find_by_id_spec.rb +8 -8
  165. data/lib/capybara/spec/session/find_field_spec.rb +33 -31
  166. data/lib/capybara/spec/session/find_link_spec.rb +42 -14
  167. data/lib/capybara/spec/session/find_spec.rb +251 -142
  168. data/lib/capybara/spec/session/first_spec.rb +45 -44
  169. data/lib/capybara/spec/session/frame/frame_title_spec.rb +6 -6
  170. data/lib/capybara/spec/session/frame/frame_url_spec.rb +6 -6
  171. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +32 -20
  172. data/lib/capybara/spec/session/frame/within_frame_spec.rb +46 -19
  173. data/lib/capybara/spec/session/go_back_spec.rb +1 -1
  174. data/lib/capybara/spec/session/go_forward_spec.rb +1 -1
  175. data/lib/capybara/spec/session/has_all_selectors_spec.rb +23 -23
  176. data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
  177. data/lib/capybara/spec/session/has_any_selectors_spec.rb +29 -0
  178. data/lib/capybara/spec/session/has_button_spec.rb +98 -12
  179. data/lib/capybara/spec/session/has_css_spec.rb +271 -137
  180. data/lib/capybara/spec/session/has_current_path_spec.rb +50 -35
  181. data/lib/capybara/spec/session/has_element_spec.rb +47 -0
  182. data/lib/capybara/spec/session/has_field_spec.rb +137 -58
  183. data/lib/capybara/spec/session/has_link_spec.rb +46 -6
  184. data/lib/capybara/spec/session/has_none_selectors_spec.rb +33 -31
  185. data/lib/capybara/spec/session/has_select_spec.rb +84 -50
  186. data/lib/capybara/spec/session/has_selector_spec.rb +117 -69
  187. data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
  188. data/lib/capybara/spec/session/has_table_spec.rb +181 -4
  189. data/lib/capybara/spec/session/has_text_spec.rb +101 -53
  190. data/lib/capybara/spec/session/has_title_spec.rb +19 -14
  191. data/lib/capybara/spec/session/has_xpath_spec.rb +56 -38
  192. data/lib/capybara/spec/session/{headers.rb → headers_spec.rb} +1 -1
  193. data/lib/capybara/spec/session/html_spec.rb +13 -6
  194. data/lib/capybara/spec/session/matches_style_spec.rb +37 -0
  195. data/lib/capybara/spec/session/node_spec.rb +958 -122
  196. data/lib/capybara/spec/session/node_wrapper_spec.rb +15 -12
  197. data/lib/capybara/spec/session/refresh_spec.rb +9 -7
  198. data/lib/capybara/spec/session/reset_session_spec.rb +65 -37
  199. data/lib/capybara/spec/session/{response_code.rb → response_code_spec.rb} +1 -1
  200. data/lib/capybara/spec/session/save_and_open_page_spec.rb +2 -2
  201. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +5 -4
  202. data/lib/capybara/spec/session/save_page_spec.rb +41 -38
  203. data/lib/capybara/spec/session/save_screenshot_spec.rb +13 -11
  204. data/lib/capybara/spec/session/screenshot_spec.rb +2 -2
  205. data/lib/capybara/spec/session/scroll_spec.rb +119 -0
  206. data/lib/capybara/spec/session/select_spec.rb +102 -76
  207. data/lib/capybara/spec/session/selectors_spec.rb +51 -18
  208. data/lib/capybara/spec/session/sibling_spec.rb +9 -9
  209. data/lib/capybara/spec/session/text_spec.rb +26 -24
  210. data/lib/capybara/spec/session/title_spec.rb +8 -6
  211. data/lib/capybara/spec/session/uncheck_spec.rb +41 -22
  212. data/lib/capybara/spec/session/unselect_spec.rb +37 -37
  213. data/lib/capybara/spec/session/visit_spec.rb +79 -53
  214. data/lib/capybara/spec/session/window/become_closed_spec.rb +22 -19
  215. data/lib/capybara/spec/session/window/current_window_spec.rb +4 -3
  216. data/lib/capybara/spec/session/window/open_new_window_spec.rb +4 -3
  217. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +25 -21
  218. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +10 -5
  219. data/lib/capybara/spec/session/window/window_spec.rb +88 -54
  220. data/lib/capybara/spec/session/window/windows_spec.rb +11 -8
  221. data/lib/capybara/spec/session/window/within_window_spec.rb +17 -16
  222. data/lib/capybara/spec/session/within_spec.rb +82 -44
  223. data/lib/capybara/spec/spec_helper.rb +46 -52
  224. data/lib/capybara/spec/test_app.rb +148 -41
  225. data/lib/capybara/spec/views/animated.erb +49 -0
  226. data/lib/capybara/spec/views/form.erb +156 -42
  227. data/lib/capybara/spec/views/frame_child.erb +4 -3
  228. data/lib/capybara/spec/views/frame_one.erb +2 -1
  229. data/lib/capybara/spec/views/frame_parent.erb +1 -1
  230. data/lib/capybara/spec/views/frame_two.erb +1 -1
  231. data/lib/capybara/spec/views/initial_alert.erb +2 -1
  232. data/lib/capybara/spec/views/layout.erb +10 -0
  233. data/lib/capybara/spec/views/obscured.erb +47 -0
  234. data/lib/capybara/spec/views/offset.erb +33 -0
  235. data/lib/capybara/spec/views/path.erb +2 -2
  236. data/lib/capybara/spec/views/popup_one.erb +1 -1
  237. data/lib/capybara/spec/views/popup_two.erb +1 -1
  238. data/lib/capybara/spec/views/react.erb +45 -0
  239. data/lib/capybara/spec/views/scroll.erb +21 -0
  240. data/lib/capybara/spec/views/spatial.erb +31 -0
  241. data/lib/capybara/spec/views/tables.erb +68 -1
  242. data/lib/capybara/spec/views/with_animation.erb +81 -0
  243. data/lib/capybara/spec/views/with_base_tag.erb +2 -2
  244. data/lib/capybara/spec/views/with_dragula.erb +24 -0
  245. data/lib/capybara/spec/views/with_fixed_header_footer.erb +2 -1
  246. data/lib/capybara/spec/views/with_hover.erb +3 -2
  247. data/lib/capybara/spec/views/with_hover1.erb +10 -0
  248. data/lib/capybara/spec/views/with_html.erb +46 -11
  249. data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
  250. data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
  251. data/lib/capybara/spec/views/with_js.erb +30 -5
  252. data/lib/capybara/spec/views/with_jstree.erb +26 -0
  253. data/lib/capybara/spec/views/with_namespace.erb +21 -0
  254. data/lib/capybara/spec/views/with_scope.erb +2 -2
  255. data/lib/capybara/spec/views/with_scope_other.erb +6 -0
  256. data/lib/capybara/spec/views/with_shadow.erb +31 -0
  257. data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
  258. data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
  259. data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
  260. data/lib/capybara/spec/views/with_windows.erb +1 -1
  261. data/lib/capybara/spec/views/within_frames.erb +1 -1
  262. data/lib/capybara/version.rb +1 -1
  263. data/lib/capybara/window.rb +32 -26
  264. data/lib/capybara.rb +128 -104
  265. data/spec/basic_node_spec.rb +59 -34
  266. data/spec/capybara_spec.rb +65 -51
  267. data/spec/counter_spec.rb +35 -0
  268. data/spec/css_builder_spec.rb +101 -0
  269. data/spec/css_splitter_spec.rb +38 -0
  270. data/spec/dsl_spec.rb +84 -55
  271. data/spec/filter_set_spec.rb +24 -7
  272. data/spec/fixtures/certificate.pem +25 -0
  273. data/spec/fixtures/key.pem +27 -0
  274. data/spec/fixtures/selenium_driver_rspec_failure.rb +5 -5
  275. data/spec/fixtures/selenium_driver_rspec_success.rb +5 -5
  276. data/spec/minitest_spec.rb +49 -5
  277. data/spec/minitest_spec_spec.rb +92 -62
  278. data/spec/per_session_config_spec.rb +6 -6
  279. data/spec/rack_test_spec.rb +183 -115
  280. data/spec/regexp_dissassembler_spec.rb +250 -0
  281. data/spec/result_spec.rb +99 -39
  282. data/spec/rspec/features_spec.rb +28 -25
  283. data/spec/rspec/scenarios_spec.rb +10 -6
  284. data/spec/rspec/shared_spec_matchers.rb +418 -364
  285. data/spec/rspec/views_spec.rb +4 -3
  286. data/spec/rspec_matchers_spec.rb +35 -10
  287. data/spec/rspec_spec.rb +109 -85
  288. data/spec/sauce_spec_chrome.rb +43 -0
  289. data/spec/selector_spec.rb +392 -62
  290. data/spec/selenium_spec_chrome.rb +183 -41
  291. data/spec/selenium_spec_chrome_remote.rb +96 -0
  292. data/spec/selenium_spec_edge.rb +41 -8
  293. data/spec/selenium_spec_firefox.rb +228 -0
  294. data/spec/selenium_spec_firefox_remote.rb +94 -0
  295. data/spec/selenium_spec_ie.rb +129 -11
  296. data/spec/selenium_spec_safari.rb +162 -0
  297. data/spec/server_spec.rb +192 -81
  298. data/spec/session_spec.rb +52 -16
  299. data/spec/shared_selenium_node.rb +79 -0
  300. data/spec/shared_selenium_session.rb +460 -123
  301. data/spec/spec_helper.rb +124 -2
  302. data/spec/whitespace_normalizer_spec.rb +54 -0
  303. data/spec/xpath_builder_spec.rb +93 -0
  304. metadata +344 -45
  305. data/.yard/templates_custom/default/class/html/selectors.erb +0 -38
  306. data/.yard/templates_custom/default/class/html/setup.rb +0 -17
  307. data/.yard/yard_extensions.rb +0 -78
  308. data/lib/capybara/rspec/compound.rb +0 -90
  309. data/lib/capybara/spec/session/assert_selector.rb +0 -149
  310. data/lib/capybara/spec/session/source_spec.rb +0 -0
  311. data/lib/capybara/spec/views/with_title.erb +0 -5
  312. data/spec/selenium_spec_marionette.rb +0 -143
@@ -28,8 +28,9 @@ module Capybara
28
28
  #
29
29
  # @return [String] The text of the element
30
30
  #
31
- def text(_type = nil)
32
- native.text
31
+ def text(_type = nil, normalize_ws: false)
32
+ txt = native.text
33
+ normalize_ws ? txt.gsub(/[[:space:]]+/, ' ').strip : txt
33
34
  end
34
35
 
35
36
  ##
@@ -45,7 +46,7 @@ module Capybara
45
46
  attr_name = name.to_s
46
47
  if attr_name == 'value'
47
48
  value
48
- elsif tag_name == 'input' and native[:type] == 'checkbox' and attr_name == 'checked'
49
+ elsif (tag_name == 'input') && (native[:type] == 'checkbox') && (attr_name == 'checked')
49
50
  native['checked'] == 'checked'
50
51
  else
51
52
  native[attr_name]
@@ -78,11 +79,11 @@ module Capybara
78
79
  if tag_name == 'textarea'
79
80
  native['_capybara_raw_value']
80
81
  elsif tag_name == 'select'
81
- if native['multiple'] == 'multiple'
82
- native.xpath(".//option[@selected='selected']").map { |option| option[:value] || option.content }
82
+ selected_options = find_xpath('.//option[@selected]')
83
+ if multiple?
84
+ selected_options.map(&method(:option_value))
83
85
  else
84
- option = native.xpath(".//option[@selected='selected']").first || native.xpath(".//option").first
85
- option[:value] || option.content if option
86
+ option_value(selected_options.first || find_xpath('.//option').first)
86
87
  end
87
88
  elsif tag_name == 'input' && %w[radio checkbox].include?(native[:type])
88
89
  native[:value] || 'on'
@@ -99,14 +100,17 @@ module Capybara
99
100
  # @param [Boolean] check_ancestors Whether to inherit visibility from ancestors
100
101
  # @return [Boolean] Whether the element is visible
101
102
  #
102
- def visible?(check_ancestors = true)
103
- return false if (tag_name == 'input') && (native[:type] == "hidden")
103
+ def visible?(check_ancestors = true) # rubocop:disable Style/OptionalBooleanParameter
104
+ return false if (tag_name == 'input') && (native[:type] == 'hidden')
105
+ return false if tag_name == 'template'
104
106
 
105
107
  if check_ancestors
106
- !native.xpath("boolean(./ancestor-or-self::*[contains(@style, 'display:none') or contains(@style, 'display: none') or @hidden or name()='script' or name()='head'])")
108
+ !find_xpath(VISIBILITY_XPATH)
107
109
  else
108
110
  # No need for an xpath if only checking the current element
109
- !(native.has_attribute?('hidden') || (native[:style] =~ /display:\s?none/) || %w[script head].include?(tag_name))
111
+ !(native.key?('hidden') ||
112
+ /display:\s?none/.match?(native[:style] || '') ||
113
+ %w[script head style].include?(tag_name))
110
114
  end
111
115
  end
112
116
 
@@ -126,7 +130,8 @@ module Capybara
126
130
  #
127
131
  # @return [Boolean] Whether the element is disabled
128
132
  def disabled?
129
- native.has_attribute?('disabled')
133
+ native.has_attribute?('disabled') &&
134
+ %w[button input select textarea optgroup option menuitem fieldset].include?(tag_name)
130
135
  end
131
136
 
132
137
  ##
@@ -139,11 +144,19 @@ module Capybara
139
144
  native.has_attribute?('selected')
140
145
  end
141
146
 
147
+ def multiple?
148
+ native.has_attribute?('multiple')
149
+ end
150
+
151
+ def readonly?
152
+ native.has_attribute?('readonly')
153
+ end
154
+
142
155
  def synchronize(_seconds = nil)
143
156
  yield # simple nodes don't need to wait
144
157
  end
145
158
 
146
- def allow_reload!
159
+ def allow_reload!(*)
147
160
  # no op
148
161
  end
149
162
 
@@ -159,12 +172,12 @@ module Capybara
159
172
  end
160
173
 
161
174
  # @api private
162
- def find_css(css)
175
+ def find_css(css, **_options)
163
176
  native.css(css)
164
177
  end
165
178
 
166
179
  # @api private
167
- def find_xpath(xpath)
180
+ def find_xpath(xpath, **_options)
168
181
  native.xpath(xpath)
169
182
  end
170
183
 
@@ -172,6 +185,32 @@ module Capybara
172
185
  def session_options
173
186
  Capybara.session_options
174
187
  end
188
+
189
+ # @api private
190
+ def initial_cache
191
+ {}
192
+ end
193
+
194
+ def ==(other)
195
+ eql?(other) || (other.respond_to?(:native) && native == other.native)
196
+ end
197
+
198
+ private
199
+
200
+ def option_value(option)
201
+ return nil if option.nil?
202
+
203
+ option[:value] || option.content
204
+ end
205
+
206
+ VISIBILITY_XPATH = XPath.generate do |x|
207
+ x.ancestor_or_self[
208
+ x.attr(:style)[x.contains('display:none') | x.contains('display: none')] |
209
+ x.attr(:hidden) |
210
+ x.qname.one_of('script', 'head') |
211
+ (~x.self(:summary) & XPath.parent(:details)[!XPath.attr(:open)])
212
+ ].boolean
213
+ end.to_s.freeze
175
214
  end
176
215
  end
177
216
  end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capybara
4
+ module Node
5
+ ##
6
+ #
7
+ # {Capybara::Node::WhitespaceNormalizer} provides methods that
8
+ # help to normalize the spacing of text content inside of
9
+ # {Capybara::Node::Element}s by removing various unicode
10
+ # spacing and directional markings.
11
+ #
12
+ module WhitespaceNormalizer
13
+ # Unicode for NBSP, or  
14
+ NON_BREAKING_SPACE = "\u00a0"
15
+ LINE_SEPERATOR = "\u2028"
16
+ PARAGRAPH_SEPERATOR = "\u2029"
17
+
18
+ # All spaces except for NBSP
19
+ BREAKING_SPACES = "[[:space:]&&[^#{NON_BREAKING_SPACE}]]".freeze
20
+
21
+ # Whitespace we want to substitute with plain spaces
22
+ SQUEEZED_SPACES = " \n\f\t\v#{LINE_SEPERATOR}#{PARAGRAPH_SEPERATOR}".freeze
23
+
24
+ # Any whitespace at the front of text
25
+ LEADING_SPACES = /\A#{BREAKING_SPACES}+/
26
+
27
+ # Any whitespace at the end of text
28
+ TRAILING_SPACES = /#{BREAKING_SPACES}+\z/
29
+
30
+ # "Invisible" space character
31
+ ZERO_WIDTH_SPACE = "\u200b"
32
+
33
+ # Signifies text is read left to right
34
+ LEFT_TO_RIGHT_MARK = "\u200e"
35
+
36
+ # Signifies text is read right to left
37
+ RIGHT_TO_LEFT_MARK = "\u200f"
38
+
39
+ # Characters we want to truncate from text
40
+ REMOVED_CHARACTERS = [ZERO_WIDTH_SPACE, LEFT_TO_RIGHT_MARK, RIGHT_TO_LEFT_MARK].join
41
+
42
+ # Matches multiple empty lines
43
+ EMPTY_LINES = /[\ \n]*\n[\ \n]*/
44
+
45
+ ##
46
+ #
47
+ # Normalizes the spacing of a node's text to be similar to
48
+ # what matchers might expect.
49
+ #
50
+ # @param text [String]
51
+ # @return [String]
52
+ #
53
+ def normalize_spacing(text)
54
+ text
55
+ .delete(REMOVED_CHARACTERS)
56
+ .tr(SQUEEZED_SPACES, ' ')
57
+ .squeeze(' ')
58
+ .sub(LEADING_SPACES, '')
59
+ .sub(TRAILING_SPACES, '')
60
+ .tr(NON_BREAKING_SPACE, ' ')
61
+ end
62
+
63
+ ##
64
+ #
65
+ # Variant on {Capybara::Node::Normalizer#normalize_spacing} that
66
+ # targets the whitespace of visible elements only.
67
+ #
68
+ # @param text [String]
69
+ # @return [String]
70
+ #
71
+ def normalize_visible_spacing(text)
72
+ text
73
+ .squeeze(' ')
74
+ .gsub(EMPTY_LINES, "\n")
75
+ .sub(LEADING_SPACES, '')
76
+ .sub(TRAILING_SPACES, '')
77
+ .tr(NON_BREAKING_SPACE, ' ')
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capybara
4
+ # @api private
5
+ module Queries
6
+ class ActiveElementQuery < BaseQuery
7
+ def initialize(**options)
8
+ @options = options
9
+ super(@options)
10
+ end
11
+
12
+ def resolve_for(session)
13
+ node = session.driver.active_element
14
+ [Capybara::Node::Element.new(session, node, nil, self)]
15
+ end
16
+ end
17
+ end
18
+ end
@@ -6,24 +6,23 @@ module Capybara
6
6
  # @api private
7
7
  def resolve_for(node, exact = nil)
8
8
  @child_node = node
9
+
9
10
  node.synchronize do
10
- match_results = super(node.session.current_scope, exact)
11
- node.all(:xpath, XPath.ancestor) { |el| match_results.include?(el) }
11
+ scope = node.respond_to?(:session) ? node.session.current_scope : node.find(:xpath, '/*')
12
+ match_results = super(scope, exact)
13
+ ancestors = node.find_xpath(XPath.ancestor.to_s)
14
+ .map(&method(:to_element))
15
+ .select { |el| match_results.include?(el) }
16
+ Capybara::Result.new(ordered_results(ancestors), self)
12
17
  end
13
18
  end
14
19
 
15
- def description
16
- child_query = @child_node && @child_node.instance_variable_get(:@query)
20
+ def description(applied = false) # rubocop:disable Style/OptionalBooleanParameter
21
+ child_query = @child_node&.instance_variable_get(:@query)
17
22
  desc = super
18
23
  desc += " that is an ancestor of #{child_query.description}" if child_query
19
24
  desc
20
25
  end
21
-
22
- private
23
-
24
- def valid_keys
25
- super - COUNT_KEYS
26
- end
27
26
  end
28
27
  end
29
28
  end
@@ -23,9 +23,9 @@ module Capybara
23
23
 
24
24
  def self.wait(options, default = Capybara.default_max_wait_time)
25
25
  # if no value or nil for the :wait option is passed it should default to the default
26
- w = options.fetch(:wait, nil)
27
- w = default if w.nil?
28
- w || 0
26
+ wait = options.fetch(:wait, nil)
27
+ wait = default if wait.nil?
28
+ wait || 0
29
29
  end
30
30
 
31
31
  ##
@@ -51,6 +51,7 @@ module Capybara
51
51
  return false if options[:maximum] && (Integer(options[:maximum]) < count)
52
52
  return false if options[:minimum] && (Integer(options[:minimum]) > count)
53
53
  return false if options[:between] && !options[:between].include?(count)
54
+
54
55
  true
55
56
  end
56
57
 
@@ -59,40 +60,46 @@ module Capybara
59
60
  # Generates a failure message from the query description and count options.
60
61
  #
61
62
  def failure_message
62
- String.new("expected to find #{description}") << count_message
63
+ +"expected to find #{description}" << count_message
63
64
  end
64
65
 
65
66
  def negative_failure_message
66
- String.new("expected not to find #{description}") << count_message
67
+ +"expected not to find #{description}" << count_message
67
68
  end
68
69
 
69
70
  private
70
71
 
71
72
  def count_specified?
72
- COUNT_KEYS.any? { |k| options.key? k }
73
+ COUNT_KEYS.any? { |key| options.key? key }
73
74
  end
74
75
 
75
76
  def count_message
76
- message = "".dup
77
- if options[:count]
78
- message << " #{options[:count]} #{Capybara::Helpers.declension('time', 'times', options[:count])}"
79
- elsif options[:between]
80
- message << " between #{options[:between].first} and #{options[:between].last} times"
81
- elsif options[:maximum]
82
- message << " at most #{options[:maximum]} #{Capybara::Helpers.declension('time', 'times', options[:maximum])}"
83
- elsif options[:minimum]
84
- message << " at least #{options[:minimum]} #{Capybara::Helpers.declension('time', 'times', options[:minimum])}"
77
+ message = +''
78
+ count, between, maximum, minimum = options.values_at(:count, :between, :maximum, :minimum)
79
+ if count
80
+ message << " #{occurrences count}"
81
+ elsif between
82
+ message << " between #{between.begin ? between.first : 1} and " \
83
+ "#{between.end ? between.last : 'infinite'} times"
84
+ elsif maximum
85
+ message << " at most #{occurrences maximum}"
86
+ elsif minimum
87
+ message << " at least #{occurrences minimum}"
85
88
  end
86
89
  message
87
90
  end
88
91
 
92
+ def occurrences(count)
93
+ "#{count} #{Capybara::Helpers.declension('time', 'times', count)}"
94
+ end
95
+
89
96
  def assert_valid_keys
90
97
  invalid_keys = @options.keys - valid_keys
91
98
  return if invalid_keys.empty?
92
99
 
93
- invalid_names = invalid_keys.map(&:inspect).join(", ")
94
- valid_names = valid_keys.map(&:inspect).join(", ")
95
- raise ArgumentError, "invalid keys #{invalid_names}, should be one of #{valid_names}"
100
+ invalid_names = invalid_keys.map(&:inspect).join(', ')
101
+ valid_names = valid_keys.map(&:inspect).join(', ')
102
+ raise ArgumentError, "Invalid option(s) #{invalid_names}, should be one of #{valid_names}"
96
103
  end
97
104
  end
98
105
  end
@@ -6,26 +6,30 @@ module Capybara
6
6
  # @api private
7
7
  module Queries
8
8
  class CurrentPathQuery < BaseQuery
9
- def initialize(expected_path, **options)
9
+ def initialize(expected_path, **options, &optional_filter_block)
10
10
  super(options)
11
11
  @expected_path = expected_path
12
12
  @options = {
13
- url: !@expected_path.is_a?(Regexp) && !::Addressable::URI.parse(@expected_path || "").hostname.nil?,
13
+ url: !@expected_path.is_a?(Regexp) && !::Addressable::URI.parse(@expected_path || '').hostname.nil?,
14
14
  ignore_query: false
15
15
  }.merge(options)
16
+ @filter_block = optional_filter_block
16
17
  assert_valid_keys
17
18
  end
18
19
 
19
20
  def resolves_for?(session)
20
21
  uri = ::Addressable::URI.parse(session.current_url)
21
- uri.query = nil if uri && options[:ignore_query]
22
- @actual_path = options[:url] ? uri.to_s : uri && uri.request_uri
22
+ @actual_path = (options[:ignore_query] ? uri&.omit(:query) : uri).then do |u|
23
+ options[:url] ? u&.to_s : u&.request_uri
24
+ end
23
25
 
24
- if @expected_path.is_a? Regexp
25
- @actual_path.to_s.match(@expected_path)
26
+ res = if @expected_path.is_a? Regexp
27
+ @actual_path.to_s.match?(@expected_path)
26
28
  else
27
29
  ::Addressable::URI.parse(@expected_path) == ::Addressable::URI.parse(@actual_path)
28
30
  end
31
+
32
+ res && matches_filter_block?(uri)
29
33
  end
30
34
 
31
35
  def failure_message
@@ -38,6 +42,12 @@ module Capybara
38
42
 
39
43
  private
40
44
 
45
+ def matches_filter_block?(url)
46
+ return true unless @filter_block
47
+
48
+ @filter_block.call(url)
49
+ end
50
+
41
51
  def failure_message_helper(negated = '')
42
52
  verb = @expected_path.is_a?(Regexp) ? 'match' : 'equal'
43
53
  "expected #{@actual_path.inspect}#{negated} to #{verb} #{@expected_path.inspect}"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Capybara
2
4
  module Queries
3
5
  class MatchQuery < Capybara::Queries::SelectorQuery
@@ -7,6 +9,15 @@ module Capybara
7
9
 
8
10
  private
9
11
 
12
+ def assert_valid_keys
13
+ invalid_options = @options.keys & COUNT_KEYS
14
+ unless invalid_options.empty?
15
+ raise ArgumentError, "Match queries don't support quantity options. Invalid keys - #{invalid_options.join(', ')}"
16
+ end
17
+
18
+ super
19
+ end
20
+
10
21
  def valid_keys
11
22
  super - COUNT_KEYS
12
23
  end