capybara 2.7.0 → 3.35.3

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 (318) hide show
  1. checksums.yaml +5 -5
  2. data/.yardopts +1 -0
  3. data/History.md +1147 -11
  4. data/License.txt +1 -1
  5. data/README.md +252 -131
  6. data/lib/capybara/config.rb +92 -0
  7. data/lib/capybara/cucumber.rb +3 -3
  8. data/lib/capybara/driver/base.rb +52 -21
  9. data/lib/capybara/driver/node.rb +48 -14
  10. data/lib/capybara/dsl.rb +16 -9
  11. data/lib/capybara/helpers.rb +72 -81
  12. data/lib/capybara/minitest/spec.rb +267 -0
  13. data/lib/capybara/minitest.rb +385 -0
  14. data/lib/capybara/node/actions.rb +337 -89
  15. data/lib/capybara/node/base.rb +50 -32
  16. data/lib/capybara/node/document.rb +19 -3
  17. data/lib/capybara/node/document_matchers.rb +22 -24
  18. data/lib/capybara/node/element.rb +388 -125
  19. data/lib/capybara/node/finders.rb +231 -121
  20. data/lib/capybara/node/matchers.rb +503 -217
  21. data/lib/capybara/node/simple.rb +64 -27
  22. data/lib/capybara/queries/ancestor_query.rb +27 -0
  23. data/lib/capybara/queries/base_query.rb +87 -11
  24. data/lib/capybara/queries/current_path_query.rb +24 -24
  25. data/lib/capybara/queries/match_query.rb +15 -10
  26. data/lib/capybara/queries/selector_query.rb +675 -81
  27. data/lib/capybara/queries/sibling_query.rb +26 -0
  28. data/lib/capybara/queries/style_query.rb +45 -0
  29. data/lib/capybara/queries/text_query.rb +88 -20
  30. data/lib/capybara/queries/title_query.rb +9 -11
  31. data/lib/capybara/rack_test/browser.rb +63 -39
  32. data/lib/capybara/rack_test/css_handlers.rb +6 -4
  33. data/lib/capybara/rack_test/driver.rb +26 -16
  34. data/lib/capybara/rack_test/errors.rb +6 -0
  35. data/lib/capybara/rack_test/form.rb +73 -58
  36. data/lib/capybara/rack_test/node.rb +187 -67
  37. data/lib/capybara/rails.rb +4 -8
  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 +142 -14
  43. data/lib/capybara/rspec/features.rb +17 -42
  44. data/lib/capybara/rspec/matcher_proxies.rb +82 -0
  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 +143 -244
  59. data/lib/capybara/rspec.rb +10 -12
  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 +102 -0
  63. data/lib/capybara/selector/definition/button.rb +63 -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 +278 -0
  85. data/lib/capybara/selector/filter.rb +3 -46
  86. data/lib/capybara/selector/filter_set.rb +124 -0
  87. data/lib/capybara/selector/filters/base.rb +77 -0
  88. data/lib/capybara/selector/filters/expression_filter.rb +22 -0
  89. data/lib/capybara/selector/filters/locator_filter.rb +29 -0
  90. data/lib/capybara/selector/filters/node_filter.rb +31 -0
  91. data/lib/capybara/selector/regexp_disassembler.rb +214 -0
  92. data/lib/capybara/selector/selector.rb +155 -0
  93. data/lib/capybara/selector/xpath_extensions.rb +17 -0
  94. data/lib/capybara/selector.rb +232 -369
  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 +380 -142
  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 +528 -97
  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 +63 -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 +74 -71
  127. data/lib/capybara/session/config.rb +126 -0
  128. data/lib/capybara/session/matchers.rb +44 -27
  129. data/lib/capybara/session.rb +500 -297
  130. data/lib/capybara/spec/fixtures/no_extension +1 -0
  131. data/lib/capybara/spec/public/jquery.js +5 -5
  132. data/lib/capybara/spec/public/offset.js +6 -0
  133. data/lib/capybara/spec/public/test.js +168 -14
  134. data/lib/capybara/spec/session/accept_alert_spec.rb +37 -14
  135. data/lib/capybara/spec/session/accept_confirm_spec.rb +7 -6
  136. data/lib/capybara/spec/session/accept_prompt_spec.rb +38 -10
  137. data/lib/capybara/spec/session/all_spec.rb +179 -59
  138. data/lib/capybara/spec/session/ancestor_spec.rb +88 -0
  139. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +140 -0
  140. data/lib/capybara/spec/session/assert_current_path_spec.rb +75 -0
  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_spec.rb +258 -0
  144. data/lib/capybara/spec/session/assert_title_spec.rb +93 -0
  145. data/lib/capybara/spec/session/attach_file_spec.rb +154 -48
  146. data/lib/capybara/spec/session/body_spec.rb +12 -13
  147. data/lib/capybara/spec/session/check_spec.rb +168 -41
  148. data/lib/capybara/spec/session/choose_spec.rb +75 -23
  149. data/lib/capybara/spec/session/click_button_spec.rb +243 -175
  150. data/lib/capybara/spec/session/click_link_or_button_spec.rb +57 -32
  151. data/lib/capybara/spec/session/click_link_spec.rb +100 -53
  152. data/lib/capybara/spec/session/current_scope_spec.rb +11 -10
  153. data/lib/capybara/spec/session/current_url_spec.rb +61 -35
  154. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +7 -7
  155. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +5 -4
  156. data/lib/capybara/spec/session/element/{assert_match_selector.rb → assert_match_selector_spec.rb} +13 -6
  157. data/lib/capybara/spec/session/element/match_css_spec.rb +21 -7
  158. data/lib/capybara/spec/session/element/match_xpath_spec.rb +9 -7
  159. data/lib/capybara/spec/session/element/matches_selector_spec.rb +91 -34
  160. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +23 -0
  161. data/lib/capybara/spec/session/evaluate_script_spec.rb +45 -3
  162. data/lib/capybara/spec/session/execute_script_spec.rb +24 -4
  163. data/lib/capybara/spec/session/fill_in_spec.rb +166 -64
  164. data/lib/capybara/spec/session/find_button_spec.rb +37 -18
  165. data/lib/capybara/spec/session/find_by_id_spec.rb +10 -9
  166. data/lib/capybara/spec/session/find_field_spec.rb +57 -34
  167. data/lib/capybara/spec/session/find_link_spec.rb +47 -10
  168. data/lib/capybara/spec/session/find_spec.rb +290 -144
  169. data/lib/capybara/spec/session/first_spec.rb +91 -48
  170. data/lib/capybara/spec/session/frame/frame_title_spec.rb +23 -0
  171. data/lib/capybara/spec/session/frame/frame_url_spec.rb +23 -0
  172. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +116 -0
  173. data/lib/capybara/spec/session/frame/within_frame_spec.rb +112 -0
  174. data/lib/capybara/spec/session/go_back_spec.rb +3 -2
  175. data/lib/capybara/spec/session/go_forward_spec.rb +3 -2
  176. data/lib/capybara/spec/session/has_all_selectors_spec.rb +69 -0
  177. data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
  178. data/lib/capybara/spec/session/has_any_selectors_spec.rb +25 -0
  179. data/lib/capybara/spec/session/has_button_spec.rb +76 -19
  180. data/lib/capybara/spec/session/has_css_spec.rb +277 -131
  181. data/lib/capybara/spec/session/has_current_path_spec.rb +98 -26
  182. data/lib/capybara/spec/session/has_field_spec.rb +177 -107
  183. data/lib/capybara/spec/session/has_link_spec.rb +13 -12
  184. data/lib/capybara/spec/session/has_none_selectors_spec.rb +78 -0
  185. data/lib/capybara/spec/session/has_select_spec.rb +191 -95
  186. data/lib/capybara/spec/session/has_selector_spec.rb +128 -64
  187. data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
  188. data/lib/capybara/spec/session/has_table_spec.rb +172 -5
  189. data/lib/capybara/spec/session/has_text_spec.rb +126 -60
  190. data/lib/capybara/spec/session/has_title_spec.rb +35 -12
  191. data/lib/capybara/spec/session/has_xpath_spec.rb +74 -53
  192. data/lib/capybara/spec/session/{headers.rb → headers_spec.rb} +3 -2
  193. data/lib/capybara/spec/session/html_spec.rb +14 -6
  194. data/lib/capybara/spec/session/matches_style_spec.rb +35 -0
  195. data/lib/capybara/spec/session/node_spec.rb +1028 -131
  196. data/lib/capybara/spec/session/node_wrapper_spec.rb +39 -0
  197. data/lib/capybara/spec/session/refresh_spec.rb +34 -0
  198. data/lib/capybara/spec/session/reset_session_spec.rb +75 -34
  199. data/lib/capybara/spec/session/{response_code.rb → response_code_spec.rb} +2 -1
  200. data/lib/capybara/spec/session/save_and_open_page_spec.rb +3 -2
  201. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +11 -15
  202. data/lib/capybara/spec/session/save_page_spec.rb +42 -55
  203. data/lib/capybara/spec/session/save_screenshot_spec.rb +16 -14
  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 +112 -85
  207. data/lib/capybara/spec/session/selectors_spec.rb +71 -8
  208. data/lib/capybara/spec/session/sibling_spec.rb +52 -0
  209. data/lib/capybara/spec/session/text_spec.rb +38 -23
  210. data/lib/capybara/spec/session/title_spec.rb +17 -5
  211. data/lib/capybara/spec/session/uncheck_spec.rb +71 -12
  212. data/lib/capybara/spec/session/unselect_spec.rb +44 -43
  213. data/lib/capybara/spec/session/visit_spec.rb +99 -32
  214. data/lib/capybara/spec/session/window/become_closed_spec.rb +33 -29
  215. data/lib/capybara/spec/session/window/current_window_spec.rb +5 -3
  216. data/lib/capybara/spec/session/window/open_new_window_spec.rb +5 -3
  217. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +39 -30
  218. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +17 -10
  219. data/lib/capybara/spec/session/window/window_spec.rb +121 -73
  220. data/lib/capybara/spec/session/window/windows_spec.rb +12 -10
  221. data/lib/capybara/spec/session/window/within_window_spec.rb +52 -82
  222. data/lib/capybara/spec/session/within_spec.rb +76 -43
  223. data/lib/capybara/spec/spec_helper.rb +67 -33
  224. data/lib/capybara/spec/test_app.rb +85 -36
  225. data/lib/capybara/spec/views/animated.erb +49 -0
  226. data/lib/capybara/spec/views/buttons.erb +1 -1
  227. data/lib/capybara/spec/views/fieldsets.erb +1 -1
  228. data/lib/capybara/spec/views/form.erb +227 -20
  229. data/lib/capybara/spec/views/frame_child.erb +10 -2
  230. data/lib/capybara/spec/views/frame_one.erb +2 -1
  231. data/lib/capybara/spec/views/frame_parent.erb +2 -2
  232. data/lib/capybara/spec/views/frame_two.erb +1 -1
  233. data/lib/capybara/spec/views/header_links.erb +1 -1
  234. data/lib/capybara/spec/views/host_links.erb +1 -1
  235. data/lib/capybara/spec/views/initial_alert.erb +10 -0
  236. data/lib/capybara/spec/views/obscured.erb +47 -0
  237. data/lib/capybara/spec/views/offset.erb +32 -0
  238. data/lib/capybara/spec/views/path.erb +1 -1
  239. data/lib/capybara/spec/views/popup_one.erb +1 -1
  240. data/lib/capybara/spec/views/popup_two.erb +1 -1
  241. data/lib/capybara/spec/views/postback.erb +1 -1
  242. data/lib/capybara/spec/views/react.erb +45 -0
  243. data/lib/capybara/spec/views/scroll.erb +20 -0
  244. data/lib/capybara/spec/views/spatial.erb +31 -0
  245. data/lib/capybara/spec/views/tables.erb +69 -2
  246. data/lib/capybara/spec/views/with_animation.erb +82 -0
  247. data/lib/capybara/spec/views/with_base_tag.erb +1 -1
  248. data/lib/capybara/spec/views/with_count.erb +1 -1
  249. data/lib/capybara/spec/views/with_dragula.erb +24 -0
  250. data/lib/capybara/spec/views/with_fixed_header_footer.erb +17 -0
  251. data/lib/capybara/spec/views/with_hover.erb +7 -1
  252. data/lib/capybara/spec/views/with_hover1.erb +10 -0
  253. data/lib/capybara/spec/views/with_html.erb +100 -10
  254. data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
  255. data/lib/capybara/spec/views/with_html_entities.erb +1 -1
  256. data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
  257. data/lib/capybara/spec/views/with_js.erb +49 -3
  258. data/lib/capybara/spec/views/with_jstree.erb +26 -0
  259. data/lib/capybara/spec/views/with_namespace.erb +20 -0
  260. data/lib/capybara/spec/views/with_scope.erb +1 -1
  261. data/lib/capybara/spec/views/with_scope_other.erb +6 -0
  262. data/lib/capybara/spec/views/with_simple_html.erb +1 -1
  263. data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
  264. data/lib/capybara/spec/views/with_title.erb +1 -1
  265. data/lib/capybara/spec/views/with_unload_alert.erb +3 -1
  266. data/lib/capybara/spec/views/with_windows.erb +7 -1
  267. data/lib/capybara/spec/views/within_frames.erb +6 -3
  268. data/lib/capybara/version.rb +2 -1
  269. data/lib/capybara/window.rb +39 -21
  270. data/lib/capybara.rb +208 -186
  271. data/spec/basic_node_spec.rb +52 -39
  272. data/spec/capybara_spec.rb +72 -50
  273. data/spec/css_builder_spec.rb +101 -0
  274. data/spec/css_splitter_spec.rb +38 -0
  275. data/spec/dsl_spec.rb +81 -61
  276. data/spec/filter_set_spec.rb +46 -0
  277. data/spec/fixtures/capybara.csv +1 -0
  278. data/spec/fixtures/certificate.pem +25 -0
  279. data/spec/fixtures/key.pem +27 -0
  280. data/spec/fixtures/selenium_driver_rspec_failure.rb +7 -3
  281. data/spec/fixtures/selenium_driver_rspec_success.rb +7 -3
  282. data/spec/minitest_spec.rb +164 -0
  283. data/spec/minitest_spec_spec.rb +162 -0
  284. data/spec/per_session_config_spec.rb +68 -0
  285. data/spec/rack_test_spec.rb +189 -96
  286. data/spec/regexp_dissassembler_spec.rb +250 -0
  287. data/spec/result_spec.rb +143 -13
  288. data/spec/rspec/features_spec.rb +38 -32
  289. data/spec/rspec/scenarios_spec.rb +9 -7
  290. data/spec/rspec/shared_spec_matchers.rb +959 -0
  291. data/spec/rspec/views_spec.rb +9 -3
  292. data/spec/rspec_matchers_spec.rb +62 -0
  293. data/spec/rspec_spec.rb +127 -30
  294. data/spec/sauce_spec_chrome.rb +43 -0
  295. data/spec/selector_spec.rb +458 -37
  296. data/spec/selenium_spec_chrome.rb +196 -9
  297. data/spec/selenium_spec_chrome_remote.rb +100 -0
  298. data/spec/selenium_spec_edge.rb +47 -0
  299. data/spec/selenium_spec_firefox.rb +210 -0
  300. data/spec/selenium_spec_firefox_remote.rb +80 -0
  301. data/spec/selenium_spec_ie.rb +150 -0
  302. data/spec/selenium_spec_safari.rb +148 -0
  303. data/spec/server_spec.rb +200 -101
  304. data/spec/session_spec.rb +91 -0
  305. data/spec/shared_selenium_node.rb +83 -0
  306. data/spec/shared_selenium_session.rb +558 -0
  307. data/spec/spec_helper.rb +94 -2
  308. data/spec/xpath_builder_spec.rb +93 -0
  309. metadata +420 -60
  310. data/lib/capybara/query.rb +0 -7
  311. data/lib/capybara/spec/session/assert_current_path.rb +0 -60
  312. data/lib/capybara/spec/session/assert_selector.rb +0 -148
  313. data/lib/capybara/spec/session/assert_text.rb +0 -196
  314. data/lib/capybara/spec/session/assert_title.rb +0 -70
  315. data/lib/capybara/spec/session/source_spec.rb +0 -0
  316. data/lib/capybara/spec/session/within_frame_spec.rb +0 -53
  317. data/spec/rspec/matchers_spec.rb +0 -827
  318. data/spec/selenium_spec.rb +0 -151
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capybara
4
+ module Queries
5
+ class SiblingQuery < SelectorQuery
6
+ # @api private
7
+ def resolve_for(node, exact = nil)
8
+ @sibling_node = node
9
+ node.synchronize do
10
+ match_results = super(node.session.current_scope, exact)
11
+ siblings = node.find_xpath((XPath.preceding_sibling + XPath.following_sibling).to_s)
12
+ .map(&method(:to_element))
13
+ .select { |el| match_results.include?(el) }
14
+ Capybara::Result.new(ordered_results(siblings), self)
15
+ end
16
+ end
17
+
18
+ def description(applied = false) # rubocop:disable Style/OptionalBooleanParameter
19
+ desc = super
20
+ sibling_query = @sibling_node&.instance_variable_get(:@query)
21
+ desc += " that is a sibling of #{sibling_query.description}" if sibling_query
22
+ desc
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capybara
4
+ # @api private
5
+ module Queries
6
+ class StyleQuery < BaseQuery
7
+ def initialize(expected_styles, session_options:, **options)
8
+ @expected_styles = stringify_keys(expected_styles)
9
+ @options = options
10
+ @actual_styles = {}
11
+ super(@options)
12
+ self.session_options = session_options
13
+
14
+ assert_valid_keys
15
+ end
16
+
17
+ def resolves_for?(node)
18
+ @node = node
19
+ @actual_styles = node.style(*@expected_styles.keys)
20
+ @expected_styles.all? do |style, value|
21
+ if value.is_a? Regexp
22
+ value.match? @actual_styles[style]
23
+ else
24
+ @actual_styles[style] == value
25
+ end
26
+ end
27
+ end
28
+
29
+ def failure_message
30
+ +"Expected node to have styles #{@expected_styles.inspect}. " \
31
+ "Actual styles were #{@actual_styles.inspect}"
32
+ end
33
+
34
+ private
35
+
36
+ def stringify_keys(hsh)
37
+ hsh.transform_keys(&:to_s)
38
+ end
39
+
40
+ def valid_keys
41
+ %i[wait]
42
+ end
43
+ end
44
+ end
45
+ end
@@ -1,47 +1,115 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Capybara
3
4
  # @api private
4
5
  module Queries
5
6
  class TextQuery < BaseQuery
6
- def initialize(*args)
7
- @type = (args.first.is_a?(Symbol) || args.first.nil?) ? args.shift : nil
8
- @expected_text, @options = args
9
- unless @expected_text.is_a?(Regexp)
10
- @expected_text = Capybara::Helpers.normalize_whitespace(@expected_text)
7
+ def initialize(type = nil, expected_text, session_options:, **options) # rubocop:disable Style/OptionalArguments
8
+ @type = type.nil? ? default_type : type
9
+ raise ArgumentError, "#{@type} is not a valid type for a text query" unless valid_types.include?(@type)
10
+
11
+ @options = options
12
+ super(@options)
13
+ self.session_options = session_options
14
+
15
+ if expected_text.nil? && !exact?
16
+ warn 'Checking for expected text of nil is confusing and/or pointless since it will always match. '\
17
+ "Please specify a string or regexp instead. #{Capybara::Helpers.filter_backtrace(caller)}"
11
18
  end
12
- @search_regexp = Capybara::Helpers.to_regexp(@expected_text)
13
- @options ||= {}
19
+
20
+ @expected_text = expected_text.is_a?(Regexp) ? expected_text : expected_text.to_s
21
+
22
+ @search_regexp = Capybara::Helpers.to_regexp(@expected_text, exact: exact?)
23
+
14
24
  assert_valid_keys
15
25
  end
16
26
 
17
27
  def resolve_for(node)
18
- @actual_text = Capybara::Helpers.normalize_whitespace(node.text(@type))
28
+ @node = node
29
+ @actual_text = text
19
30
  @count = @actual_text.scan(@search_regexp).size
20
31
  end
21
32
 
22
33
  def failure_message
23
- description =
24
- if @expected_text.is_a?(Regexp)
25
- "text matching #{@expected_text.inspect}"
26
- else
27
- "text #{@expected_text.inspect}"
28
- end
29
-
30
- message = Capybara::Helpers.failure_message(description, @options)
34
+ super << build_message(true)
35
+ end
36
+
37
+ def negative_failure_message
38
+ super << build_message(false)
39
+ end
40
+
41
+ def description
42
+ if @expected_text.is_a?(Regexp)
43
+ "text matching #{@expected_text.inspect}"
44
+ else
45
+ "#{'exact ' if exact?}text #{@expected_text.inspect}"
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def exact?
52
+ options.fetch(:exact, session_options.exact_text)
53
+ end
54
+
55
+ def build_message(report_on_invisible)
56
+ message = +''
31
57
  unless (COUNT_KEYS & @options.keys).empty?
32
58
  message << " but found #{@count} #{Capybara::Helpers.declension('time', 'times', @count)}"
33
59
  end
34
60
  message << " in #{@actual_text.inspect}"
61
+
62
+ details_message = []
63
+ details_message << case_insensitive_message if @node && check_case_insensitive?
64
+ details_message << invisible_message if @node && check_visible_text? && report_on_invisible
65
+ details_message.compact!
66
+
67
+ message << ". (However, #{details_message.join(' and ')}.)" unless details_message.empty?
68
+ message
35
69
  end
36
70
 
37
- def negative_failure_message
38
- failure_message.sub(/(to find)/, 'not \1')
71
+ def case_insensitive_message
72
+ insensitive_regexp = Capybara::Helpers.to_regexp(@expected_text, options: Regexp::IGNORECASE)
73
+ insensitive_count = @actual_text.scan(insensitive_regexp).size
74
+ return if insensitive_count == @count
75
+
76
+ "it was found #{occurrences insensitive_count} using a case insensitive search"
39
77
  end
40
78
 
41
- private
79
+ def invisible_message
80
+ invisible_text = text(query_type: :all)
81
+ invisible_count = invisible_text.scan(@search_regexp).size
82
+ return if invisible_count == @count
83
+
84
+ "it was found #{occurrences invisible_count} including non-visible text"
85
+ rescue StandardError
86
+ # An error getting the non-visible text (if element goes out of scope) should not affect the response
87
+ nil
88
+ end
42
89
 
43
90
  def valid_keys
44
- COUNT_KEYS + [:wait]
91
+ COUNT_KEYS + %i[wait exact normalize_ws]
92
+ end
93
+
94
+ def valid_types
95
+ %i[all visible]
96
+ end
97
+
98
+ def check_visible_text?
99
+ @type == :visible
100
+ end
101
+
102
+ def check_case_insensitive?
103
+ !@expected_text.is_a?(Regexp)
104
+ end
105
+
106
+ def text(node: @node, query_type: @type)
107
+ normalize_ws = options.fetch(:normalize_ws, session_options.default_normalize_ws)
108
+ node.text(query_type, normalize_ws: normalize_ws)
109
+ end
110
+
111
+ def default_type
112
+ Capybara.ignore_hidden_elements || Capybara.visible_text_only ? :visible : :all
45
113
  end
46
114
  end
47
115
  end
@@ -1,21 +1,19 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Capybara
3
4
  # @api private
4
5
  module Queries
5
6
  class TitleQuery < BaseQuery
6
- def initialize(expected_title, options = {})
7
- @expected_title = expected_title
7
+ def initialize(expected_title, **options)
8
+ @expected_title = expected_title.is_a?(Regexp) ? expected_title : expected_title.to_s
8
9
  @options = options
9
- unless @expected_title.is_a?(Regexp)
10
- @expected_title = Capybara::Helpers.normalize_whitespace(@expected_title)
11
- end
12
- @search_regexp = Capybara::Helpers.to_regexp(@expected_title)
10
+ super(@options)
11
+ @search_regexp = Helpers.to_regexp(@expected_title, all_whitespace: true, exact: options.fetch(:exact, false))
13
12
  assert_valid_keys
14
13
  end
15
14
 
16
15
  def resolves_for?(node)
17
- @actual_title = node.title
18
- @actual_title.match(@search_regexp)
16
+ (@actual_title = node.title).match?(@search_regexp)
19
17
  end
20
18
 
21
19
  def failure_message
@@ -26,15 +24,15 @@ module Capybara
26
24
  failure_message_helper(' not')
27
25
  end
28
26
 
29
- private
27
+ private
30
28
 
31
29
  def failure_message_helper(negated = '')
32
- verb = (@expected_title.is_a?(Regexp))? 'match' : 'include'
30
+ verb = @expected_title.is_a?(Regexp) ? 'match' : 'include'
33
31
  "expected #{@actual_title.inspect}#{negated} to #{verb} #{@expected_title.inspect}"
34
32
  end
35
33
 
36
34
  def valid_keys
37
- [:wait]
35
+ %i[wait exact]
38
36
  end
39
37
  end
40
38
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  class Capybara::RackTest::Browser
3
4
  include ::Rack::Test::Methods
4
5
 
@@ -7,6 +8,7 @@ class Capybara::RackTest::Browser
7
8
 
8
9
  def initialize(driver)
9
10
  @driver = driver
11
+ @current_fragment = nil
10
12
  end
11
13
 
12
14
  def app
@@ -17,61 +19,82 @@ class Capybara::RackTest::Browser
17
19
  driver.options
18
20
  end
19
21
 
20
- def visit(path, attributes = {})
22
+ def visit(path, **attributes)
21
23
  reset_host!
22
24
  process_and_follow_redirects(:get, path, attributes)
23
25
  end
24
26
 
27
+ def refresh
28
+ reset_cache!
29
+ request(last_request.fullpath, last_request.env)
30
+ end
31
+
25
32
  def submit(method, path, attributes)
26
- path = request_path if not path or path.empty?
27
- process_and_follow_redirects(method, path, attributes, {'HTTP_REFERER' => current_url})
33
+ path = request_path if path.nil? || path.empty?
34
+ uri = build_uri(path)
35
+ uri.query = '' if method.to_s.casecmp('get').zero?
36
+ process_and_follow_redirects(method, uri.to_s, attributes, 'HTTP_REFERER' => current_url)
28
37
  end
29
38
 
30
- def follow(method, path, attributes = {})
31
- return if path.gsub(/^#{Regexp.escape(request_path)}/, '').start_with?('#') || path.downcase.start_with?('javascript:')
32
- process_and_follow_redirects(method, path, attributes, {'HTTP_REFERER' => current_url})
39
+ def follow(method, path, **attributes)
40
+ return if fragment_or_script?(path)
41
+
42
+ process_and_follow_redirects(method, path, attributes, 'HTTP_REFERER' => current_url)
33
43
  end
34
44
 
35
45
  def process_and_follow_redirects(method, path, attributes = {}, env = {})
46
+ @current_fragment = build_uri(path).fragment
36
47
  process(method, path, attributes, env)
37
- if driver.follow_redirects?
38
- driver.redirect_limit.times do
39
- process(:get, last_response["Location"], {}, env) if last_response.redirect?
48
+
49
+ return unless driver.follow_redirects?
50
+
51
+ driver.redirect_limit.times do
52
+ if last_response.redirect?
53
+ if [307, 308].include? last_response.status
54
+ process(last_request.request_method, last_response['Location'], last_request.params, env)
55
+ else
56
+ process(:get, last_response['Location'], {}, env)
57
+ end
40
58
  end
41
- raise Capybara::InfiniteRedirectError, "redirected more than #{driver.redirect_limit} times, check for infinite redirects." if last_response.redirect?
59
+ end
60
+
61
+ if last_response.redirect? # rubocop:disable Style/GuardClause
62
+ raise Capybara::InfiniteRedirectError, "redirected more than #{driver.redirect_limit} times, check for infinite redirects."
42
63
  end
43
64
  end
44
65
 
45
66
  def process(method, path, attributes = {}, env = {})
46
- new_uri = URI.parse(path)
47
- method.downcase! unless method.is_a? Symbol
48
-
49
- new_uri.path = request_path if path.start_with?("?")
50
- new_uri.path = "/" if new_uri.path.empty?
51
- new_uri.path = request_path.sub(%r(/[^/]*$), '/') + new_uri.path unless new_uri.path.start_with?('/')
52
- new_uri.scheme ||= @current_scheme
53
- new_uri.host ||= @current_host
54
- new_uri.port ||= @current_port unless new_uri.default_port == @current_port
55
-
56
- @current_scheme = new_uri.scheme
57
- @current_host = new_uri.host
58
- @current_port = new_uri.port
59
-
67
+ method = method.downcase
68
+ new_uri = build_uri(path)
69
+ @current_scheme, @current_host, @current_port = new_uri.select(:scheme, :host, :port)
70
+ @current_fragment = new_uri.fragment || @current_fragment
60
71
  reset_cache!
61
72
  send(method, new_uri.to_s, attributes, env.merge(options[:headers] || {}))
62
73
  end
63
74
 
75
+ def build_uri(path)
76
+ URI.parse(path).tap do |uri|
77
+ uri.path = request_path if path.empty? || path.start_with?('?')
78
+ uri.path = '/' if uri.path.empty?
79
+ uri.path = request_path.sub(%r{/[^/]*$}, '/') + uri.path unless uri.path.start_with?('/')
80
+
81
+ uri.scheme ||= @current_scheme
82
+ uri.host ||= @current_host
83
+ uri.port ||= @current_port unless uri.default_port == @current_port
84
+ end
85
+ end
86
+
64
87
  def current_url
65
- last_request.url
88
+ uri = build_uri(last_request.url)
89
+ uri.fragment = @current_fragment if @current_fragment
90
+ uri.to_s
66
91
  rescue Rack::Test::Error
67
- ""
92
+ ''
68
93
  end
69
94
 
70
95
  def reset_host!
71
- uri = URI.parse(Capybara.app_host || Capybara.default_host)
72
- @current_scheme = uri.scheme
73
- @current_host = uri.host
74
- @current_port = uri.port
96
+ uri = URI.parse(driver.session_options.app_host || driver.session_options.default_host)
97
+ @current_scheme, @current_host, @current_port = uri.select(:scheme, :host, :port)
75
98
  end
76
99
 
77
100
  def reset_cache!
@@ -83,7 +106,7 @@ class Capybara::RackTest::Browser
83
106
  end
84
107
 
85
108
  def find(format, selector)
86
- if format==:css
109
+ if format == :css
87
110
  dom.css(selector, Capybara::RackTest::CSSHandlers.new)
88
111
  else
89
112
  dom.xpath(selector)
@@ -93,16 +116,11 @@ class Capybara::RackTest::Browser
93
116
  def html
94
117
  last_response.body
95
118
  rescue Rack::Test::Error
96
- ""
119
+ ''
97
120
  end
98
121
 
99
122
  def title
100
- if dom.respond_to? :title
101
- dom.title
102
- else
103
- #old versions of nokogiri don't have #title - remove in 3.0
104
- dom.xpath('/html/head/title | /html/title').first.text
105
- end
123
+ dom.title
106
124
  end
107
125
 
108
126
  protected
@@ -115,6 +133,12 @@ protected
115
133
  def request_path
116
134
  last_request.path
117
135
  rescue Rack::Test::Error
118
- "/"
136
+ '/'
137
+ end
138
+
139
+ private
140
+
141
+ def fragment_or_script?(path)
142
+ path.gsub(/^#{Regexp.escape(request_path)}/, '').start_with?('#') || path.downcase.start_with?('javascript:')
119
143
  end
120
144
  end
@@ -1,11 +1,13 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  class Capybara::RackTest::CSSHandlers < BasicObject
3
4
  include ::Kernel
4
-
5
- def disabled list
5
+
6
+ def disabled(list)
6
7
  list.find_all { |node| node.has_attribute? 'disabled' }
7
- end
8
- def enabled list
8
+ end
9
+
10
+ def enabled(list)
9
11
  list.find_all { |node| !node.has_attribute? 'disabled' }
10
12
  end
11
13
  end
@@ -1,20 +1,23 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'rack/test'
3
4
  require 'rack/utils'
4
- require 'mime/types'
5
+ require 'mini_mime'
5
6
  require 'nokogiri'
6
7
  require 'cgi'
7
8
 
8
9
  class Capybara::RackTest::Driver < Capybara::Driver::Base
9
10
  DEFAULT_OPTIONS = {
10
- :respect_data_method => false,
11
- :follow_redirects => true,
12
- :redirect_limit => 5
13
- }
11
+ respect_data_method: false,
12
+ follow_redirects: true,
13
+ redirect_limit: 5
14
+ }.freeze
14
15
  attr_reader :app, :options
15
16
 
16
- def initialize(app, options={})
17
- raise ArgumentError, "rack-test requires a rack application, but none was given" unless app
17
+ def initialize(app, **options)
18
+ raise ArgumentError, 'rack-test requires a rack application, but none was given' unless app
19
+
20
+ super()
18
21
  @app = app
19
22
  @options = DEFAULT_OPTIONS.merge(options)
20
23
  end
@@ -39,15 +42,19 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
39
42
  browser.last_request
40
43
  end
41
44
 
42
- def visit(path, attributes = {})
43
- browser.visit(path, attributes)
45
+ def visit(path, **attributes)
46
+ browser.visit(path, **attributes)
47
+ end
48
+
49
+ def refresh
50
+ browser.refresh
44
51
  end
45
52
 
46
53
  def submit(method, path, attributes)
47
54
  browser.submit(method, path, attributes)
48
55
  end
49
56
 
50
- def follow(method, path, attributes = {})
57
+ def follow(method, path, **attributes)
51
58
  browser.follow(method, path, attributes)
52
59
  end
53
60
 
@@ -68,7 +75,11 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
68
75
  end
69
76
 
70
77
  def find_css(selector)
71
- browser.find(:css,selector)
78
+ browser.find(:css, selector)
79
+ rescue Nokogiri::CSS::SyntaxError
80
+ raise unless selector.include?(' i]')
81
+
82
+ raise ArgumentError, "This driver doesn't support case insensitive attribute matching when using CSS base selectors"
72
83
  end
73
84
 
74
85
  def html
@@ -87,14 +98,13 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
87
98
  @browser = nil
88
99
  end
89
100
 
90
- # @deprecated This method is being removed
91
- def browser_initialized?
92
- super && !@browser.nil?
93
- end
94
-
95
101
  def get(*args, &block); browser.get(*args, &block); end
96
102
  def post(*args, &block); browser.post(*args, &block); end
97
103
  def put(*args, &block); browser.put(*args, &block); end
98
104
  def delete(*args, &block); browser.delete(*args, &block); end
99
105
  def header(key, value); browser.header(key, value); end
106
+
107
+ def invalid_element_errors
108
+ [Capybara::RackTest::Errors::StaleElementReferenceError]
109
+ end
100
110
  end