capybara 3.32.2

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 (313) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +1 -0
  3. data/History.md +1813 -0
  4. data/License.txt +22 -0
  5. data/README.md +1099 -0
  6. data/lib/capybara.rb +511 -0
  7. data/lib/capybara/config.rb +94 -0
  8. data/lib/capybara/cucumber.rb +27 -0
  9. data/lib/capybara/driver/base.rb +170 -0
  10. data/lib/capybara/driver/node.rb +139 -0
  11. data/lib/capybara/dsl.rb +65 -0
  12. data/lib/capybara/helpers.rb +108 -0
  13. data/lib/capybara/minitest.rb +386 -0
  14. data/lib/capybara/minitest/spec.rb +264 -0
  15. data/lib/capybara/node/actions.rb +420 -0
  16. data/lib/capybara/node/base.rb +143 -0
  17. data/lib/capybara/node/document.rb +48 -0
  18. data/lib/capybara/node/document_matchers.rb +67 -0
  19. data/lib/capybara/node/element.rb +606 -0
  20. data/lib/capybara/node/finders.rb +325 -0
  21. data/lib/capybara/node/matchers.rb +883 -0
  22. data/lib/capybara/node/simple.rb +208 -0
  23. data/lib/capybara/queries/ancestor_query.rb +27 -0
  24. data/lib/capybara/queries/base_query.rb +106 -0
  25. data/lib/capybara/queries/current_path_query.rb +51 -0
  26. data/lib/capybara/queries/match_query.rb +26 -0
  27. data/lib/capybara/queries/selector_query.rb +710 -0
  28. data/lib/capybara/queries/sibling_query.rb +26 -0
  29. data/lib/capybara/queries/style_query.rb +45 -0
  30. data/lib/capybara/queries/text_query.rb +110 -0
  31. data/lib/capybara/queries/title_query.rb +39 -0
  32. data/lib/capybara/rack_test/browser.rb +140 -0
  33. data/lib/capybara/rack_test/css_handlers.rb +13 -0
  34. data/lib/capybara/rack_test/driver.rb +109 -0
  35. data/lib/capybara/rack_test/errors.rb +6 -0
  36. data/lib/capybara/rack_test/form.rb +127 -0
  37. data/lib/capybara/rack_test/node.rb +325 -0
  38. data/lib/capybara/rails.rb +16 -0
  39. data/lib/capybara/registrations/drivers.rb +36 -0
  40. data/lib/capybara/registrations/patches/puma_ssl.rb +27 -0
  41. data/lib/capybara/registrations/servers.rb +44 -0
  42. data/lib/capybara/result.rb +190 -0
  43. data/lib/capybara/rspec.rb +29 -0
  44. data/lib/capybara/rspec/features.rb +23 -0
  45. data/lib/capybara/rspec/matcher_proxies.rb +82 -0
  46. data/lib/capybara/rspec/matchers.rb +201 -0
  47. data/lib/capybara/rspec/matchers/base.rb +111 -0
  48. data/lib/capybara/rspec/matchers/become_closed.rb +33 -0
  49. data/lib/capybara/rspec/matchers/compound.rb +88 -0
  50. data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
  51. data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
  52. data/lib/capybara/rspec/matchers/have_current_path.rb +29 -0
  53. data/lib/capybara/rspec/matchers/have_selector.rb +77 -0
  54. data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
  55. data/lib/capybara/rspec/matchers/have_text.rb +33 -0
  56. data/lib/capybara/rspec/matchers/have_title.rb +29 -0
  57. data/lib/capybara/rspec/matchers/match_selector.rb +27 -0
  58. data/lib/capybara/rspec/matchers/match_style.rb +38 -0
  59. data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
  60. data/lib/capybara/selector.rb +233 -0
  61. data/lib/capybara/selector/builders/css_builder.rb +84 -0
  62. data/lib/capybara/selector/builders/xpath_builder.rb +69 -0
  63. data/lib/capybara/selector/css.rb +102 -0
  64. data/lib/capybara/selector/definition.rb +276 -0
  65. data/lib/capybara/selector/definition/button.rb +51 -0
  66. data/lib/capybara/selector/definition/checkbox.rb +26 -0
  67. data/lib/capybara/selector/definition/css.rb +10 -0
  68. data/lib/capybara/selector/definition/datalist_input.rb +35 -0
  69. data/lib/capybara/selector/definition/datalist_option.rb +25 -0
  70. data/lib/capybara/selector/definition/element.rb +27 -0
  71. data/lib/capybara/selector/definition/field.rb +40 -0
  72. data/lib/capybara/selector/definition/fieldset.rb +14 -0
  73. data/lib/capybara/selector/definition/file_field.rb +13 -0
  74. data/lib/capybara/selector/definition/fillable_field.rb +33 -0
  75. data/lib/capybara/selector/definition/frame.rb +17 -0
  76. data/lib/capybara/selector/definition/id.rb +6 -0
  77. data/lib/capybara/selector/definition/label.rb +62 -0
  78. data/lib/capybara/selector/definition/link.rb +46 -0
  79. data/lib/capybara/selector/definition/link_or_button.rb +16 -0
  80. data/lib/capybara/selector/definition/option.rb +27 -0
  81. data/lib/capybara/selector/definition/radio_button.rb +27 -0
  82. data/lib/capybara/selector/definition/select.rb +81 -0
  83. data/lib/capybara/selector/definition/table.rb +109 -0
  84. data/lib/capybara/selector/definition/table_row.rb +21 -0
  85. data/lib/capybara/selector/definition/xpath.rb +5 -0
  86. data/lib/capybara/selector/filter.rb +5 -0
  87. data/lib/capybara/selector/filter_set.rb +124 -0
  88. data/lib/capybara/selector/filters/base.rb +77 -0
  89. data/lib/capybara/selector/filters/expression_filter.rb +22 -0
  90. data/lib/capybara/selector/filters/locator_filter.rb +29 -0
  91. data/lib/capybara/selector/filters/node_filter.rb +31 -0
  92. data/lib/capybara/selector/regexp_disassembler.rb +214 -0
  93. data/lib/capybara/selector/selector.rb +147 -0
  94. data/lib/capybara/selector/xpath_extensions.rb +17 -0
  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 +496 -0
  100. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +119 -0
  101. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +126 -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 +78 -0
  110. data/lib/capybara/selenium/logger_suppressor.rb +34 -0
  111. data/lib/capybara/selenium/node.rb +610 -0
  112. data/lib/capybara/selenium/nodes/chrome_node.rb +119 -0
  113. data/lib/capybara/selenium/nodes/edge_node.rb +104 -0
  114. data/lib/capybara/selenium/nodes/firefox_node.rb +131 -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 +47 -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.rb +126 -0
  124. data/lib/capybara/server/animation_disabler.rb +58 -0
  125. data/lib/capybara/server/checker.rb +44 -0
  126. data/lib/capybara/server/middleware.rb +69 -0
  127. data/lib/capybara/session.rb +942 -0
  128. data/lib/capybara/session/config.rb +124 -0
  129. data/lib/capybara/session/matchers.rb +87 -0
  130. data/lib/capybara/spec/fixtures/another_test_file.txt +1 -0
  131. data/lib/capybara/spec/fixtures/capybara.jpg +3 -0
  132. data/lib/capybara/spec/fixtures/no_extension +1 -0
  133. data/lib/capybara/spec/fixtures/test_file.txt +1 -0
  134. data/lib/capybara/spec/public/jquery-ui.js +13 -0
  135. data/lib/capybara/spec/public/jquery.js +5 -0
  136. data/lib/capybara/spec/public/offset.js +6 -0
  137. data/lib/capybara/spec/public/test.js +268 -0
  138. data/lib/capybara/spec/session/accept_alert_spec.rb +81 -0
  139. data/lib/capybara/spec/session/accept_confirm_spec.rb +32 -0
  140. data/lib/capybara/spec/session/accept_prompt_spec.rb +78 -0
  141. data/lib/capybara/spec/session/all_spec.rb +278 -0
  142. data/lib/capybara/spec/session/ancestor_spec.rb +88 -0
  143. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +140 -0
  144. data/lib/capybara/spec/session/assert_current_path_spec.rb +75 -0
  145. data/lib/capybara/spec/session/assert_selector_spec.rb +143 -0
  146. data/lib/capybara/spec/session/assert_style_spec.rb +26 -0
  147. data/lib/capybara/spec/session/assert_text_spec.rb +258 -0
  148. data/lib/capybara/spec/session/assert_title_spec.rb +93 -0
  149. data/lib/capybara/spec/session/attach_file_spec.rb +216 -0
  150. data/lib/capybara/spec/session/body_spec.rb +23 -0
  151. data/lib/capybara/spec/session/check_spec.rb +235 -0
  152. data/lib/capybara/spec/session/choose_spec.rb +121 -0
  153. data/lib/capybara/spec/session/click_button_spec.rb +506 -0
  154. data/lib/capybara/spec/session/click_link_or_button_spec.rb +129 -0
  155. data/lib/capybara/spec/session/click_link_spec.rb +229 -0
  156. data/lib/capybara/spec/session/current_scope_spec.rb +31 -0
  157. data/lib/capybara/spec/session/current_url_spec.rb +115 -0
  158. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +36 -0
  159. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +21 -0
  160. data/lib/capybara/spec/session/element/assert_match_selector_spec.rb +38 -0
  161. data/lib/capybara/spec/session/element/match_css_spec.rb +31 -0
  162. data/lib/capybara/spec/session/element/match_xpath_spec.rb +25 -0
  163. data/lib/capybara/spec/session/element/matches_selector_spec.rb +120 -0
  164. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +23 -0
  165. data/lib/capybara/spec/session/evaluate_script_spec.rb +49 -0
  166. data/lib/capybara/spec/session/execute_script_spec.rb +28 -0
  167. data/lib/capybara/spec/session/fill_in_spec.rb +286 -0
  168. data/lib/capybara/spec/session/find_button_spec.rb +74 -0
  169. data/lib/capybara/spec/session/find_by_id_spec.rb +33 -0
  170. data/lib/capybara/spec/session/find_field_spec.rb +113 -0
  171. data/lib/capybara/spec/session/find_link_spec.rb +70 -0
  172. data/lib/capybara/spec/session/find_spec.rb +531 -0
  173. data/lib/capybara/spec/session/first_spec.rb +156 -0
  174. data/lib/capybara/spec/session/frame/frame_title_spec.rb +23 -0
  175. data/lib/capybara/spec/session/frame/frame_url_spec.rb +23 -0
  176. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +116 -0
  177. data/lib/capybara/spec/session/frame/within_frame_spec.rb +112 -0
  178. data/lib/capybara/spec/session/go_back_spec.rb +12 -0
  179. data/lib/capybara/spec/session/go_forward_spec.rb +14 -0
  180. data/lib/capybara/spec/session/has_all_selectors_spec.rb +69 -0
  181. data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
  182. data/lib/capybara/spec/session/has_any_selectors_spec.rb +25 -0
  183. data/lib/capybara/spec/session/has_button_spec.rb +69 -0
  184. data/lib/capybara/spec/session/has_css_spec.rb +374 -0
  185. data/lib/capybara/spec/session/has_current_path_spec.rb +138 -0
  186. data/lib/capybara/spec/session/has_field_spec.rb +349 -0
  187. data/lib/capybara/spec/session/has_link_spec.rb +39 -0
  188. data/lib/capybara/spec/session/has_none_selectors_spec.rb +78 -0
  189. data/lib/capybara/spec/session/has_select_spec.rb +310 -0
  190. data/lib/capybara/spec/session/has_selector_spec.rb +202 -0
  191. data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
  192. data/lib/capybara/spec/session/has_table_spec.rb +198 -0
  193. data/lib/capybara/spec/session/has_text_spec.rb +394 -0
  194. data/lib/capybara/spec/session/has_title_spec.rb +71 -0
  195. data/lib/capybara/spec/session/has_xpath_spec.rb +149 -0
  196. data/lib/capybara/spec/session/headers_spec.rb +8 -0
  197. data/lib/capybara/spec/session/html_spec.rb +47 -0
  198. data/lib/capybara/spec/session/matches_style_spec.rb +35 -0
  199. data/lib/capybara/spec/session/node_spec.rb +1292 -0
  200. data/lib/capybara/spec/session/node_wrapper_spec.rb +39 -0
  201. data/lib/capybara/spec/session/refresh_spec.rb +33 -0
  202. data/lib/capybara/spec/session/reset_session_spec.rb +148 -0
  203. data/lib/capybara/spec/session/response_code_spec.rb +8 -0
  204. data/lib/capybara/spec/session/save_and_open_page_spec.rb +21 -0
  205. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +43 -0
  206. data/lib/capybara/spec/session/save_page_spec.rb +110 -0
  207. data/lib/capybara/spec/session/save_screenshot_spec.rb +55 -0
  208. data/lib/capybara/spec/session/screenshot_spec.rb +18 -0
  209. data/lib/capybara/spec/session/scroll_spec.rb +117 -0
  210. data/lib/capybara/spec/session/select_spec.rb +229 -0
  211. data/lib/capybara/spec/session/selectors_spec.rb +98 -0
  212. data/lib/capybara/spec/session/sibling_spec.rb +52 -0
  213. data/lib/capybara/spec/session/source_spec.rb +0 -0
  214. data/lib/capybara/spec/session/text_spec.rb +74 -0
  215. data/lib/capybara/spec/session/title_spec.rb +29 -0
  216. data/lib/capybara/spec/session/uncheck_spec.rb +100 -0
  217. data/lib/capybara/spec/session/unselect_spec.rb +116 -0
  218. data/lib/capybara/spec/session/visit_spec.rb +204 -0
  219. data/lib/capybara/spec/session/window/become_closed_spec.rb +89 -0
  220. data/lib/capybara/spec/session/window/current_window_spec.rb +28 -0
  221. data/lib/capybara/spec/session/window/open_new_window_spec.rb +31 -0
  222. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +132 -0
  223. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +99 -0
  224. data/lib/capybara/spec/session/window/window_spec.rb +203 -0
  225. data/lib/capybara/spec/session/window/windows_spec.rb +34 -0
  226. data/lib/capybara/spec/session/window/within_window_spec.rb +157 -0
  227. data/lib/capybara/spec/session/within_spec.rb +199 -0
  228. data/lib/capybara/spec/spec_helper.rb +134 -0
  229. data/lib/capybara/spec/test_app.rb +226 -0
  230. data/lib/capybara/spec/views/animated.erb +49 -0
  231. data/lib/capybara/spec/views/buttons.erb +5 -0
  232. data/lib/capybara/spec/views/fieldsets.erb +30 -0
  233. data/lib/capybara/spec/views/form.erb +685 -0
  234. data/lib/capybara/spec/views/frame_child.erb +18 -0
  235. data/lib/capybara/spec/views/frame_one.erb +10 -0
  236. data/lib/capybara/spec/views/frame_parent.erb +9 -0
  237. data/lib/capybara/spec/views/frame_two.erb +9 -0
  238. data/lib/capybara/spec/views/header_links.erb +8 -0
  239. data/lib/capybara/spec/views/host_links.erb +13 -0
  240. data/lib/capybara/spec/views/initial_alert.erb +10 -0
  241. data/lib/capybara/spec/views/obscured.erb +47 -0
  242. data/lib/capybara/spec/views/offset.erb +32 -0
  243. data/lib/capybara/spec/views/path.erb +13 -0
  244. data/lib/capybara/spec/views/popup_one.erb +9 -0
  245. data/lib/capybara/spec/views/popup_two.erb +9 -0
  246. data/lib/capybara/spec/views/postback.erb +14 -0
  247. data/lib/capybara/spec/views/react.erb +45 -0
  248. data/lib/capybara/spec/views/scroll.erb +20 -0
  249. data/lib/capybara/spec/views/spatial.erb +31 -0
  250. data/lib/capybara/spec/views/tables.erb +130 -0
  251. data/lib/capybara/spec/views/with_animation.erb +74 -0
  252. data/lib/capybara/spec/views/with_base_tag.erb +11 -0
  253. data/lib/capybara/spec/views/with_count.erb +8 -0
  254. data/lib/capybara/spec/views/with_dragula.erb +22 -0
  255. data/lib/capybara/spec/views/with_fixed_header_footer.erb +17 -0
  256. data/lib/capybara/spec/views/with_hover.erb +24 -0
  257. data/lib/capybara/spec/views/with_hover1.erb +10 -0
  258. data/lib/capybara/spec/views/with_html.erb +208 -0
  259. data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
  260. data/lib/capybara/spec/views/with_html_entities.erb +2 -0
  261. data/lib/capybara/spec/views/with_js.erb +160 -0
  262. data/lib/capybara/spec/views/with_jstree.erb +26 -0
  263. data/lib/capybara/spec/views/with_namespace.erb +20 -0
  264. data/lib/capybara/spec/views/with_scope.erb +42 -0
  265. data/lib/capybara/spec/views/with_scope_other.erb +6 -0
  266. data/lib/capybara/spec/views/with_simple_html.erb +2 -0
  267. data/lib/capybara/spec/views/with_slow_unload.erb +17 -0
  268. data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
  269. data/lib/capybara/spec/views/with_title.erb +5 -0
  270. data/lib/capybara/spec/views/with_unload_alert.erb +14 -0
  271. data/lib/capybara/spec/views/with_windows.erb +54 -0
  272. data/lib/capybara/spec/views/within_frames.erb +15 -0
  273. data/lib/capybara/version.rb +5 -0
  274. data/lib/capybara/window.rb +146 -0
  275. data/spec/basic_node_spec.rb +154 -0
  276. data/spec/capybara_spec.rb +112 -0
  277. data/spec/css_builder_spec.rb +101 -0
  278. data/spec/css_splitter_spec.rb +38 -0
  279. data/spec/dsl_spec.rb +276 -0
  280. data/spec/filter_set_spec.rb +46 -0
  281. data/spec/fixtures/capybara.csv +1 -0
  282. data/spec/fixtures/certificate.pem +25 -0
  283. data/spec/fixtures/key.pem +27 -0
  284. data/spec/fixtures/selenium_driver_rspec_failure.rb +13 -0
  285. data/spec/fixtures/selenium_driver_rspec_success.rb +13 -0
  286. data/spec/minitest_spec.rb +163 -0
  287. data/spec/minitest_spec_spec.rb +162 -0
  288. data/spec/per_session_config_spec.rb +68 -0
  289. data/spec/rack_test_spec.rb +268 -0
  290. data/spec/regexp_dissassembler_spec.rb +250 -0
  291. data/spec/result_spec.rb +196 -0
  292. data/spec/rspec/features_spec.rb +99 -0
  293. data/spec/rspec/scenarios_spec.rb +19 -0
  294. data/spec/rspec/shared_spec_matchers.rb +947 -0
  295. data/spec/rspec/views_spec.rb +14 -0
  296. data/spec/rspec_matchers_spec.rb +62 -0
  297. data/spec/rspec_spec.rb +145 -0
  298. data/spec/sauce_spec_chrome.rb +43 -0
  299. data/spec/selector_spec.rb +513 -0
  300. data/spec/selenium_spec_chrome.rb +188 -0
  301. data/spec/selenium_spec_chrome_remote.rb +96 -0
  302. data/spec/selenium_spec_edge.rb +47 -0
  303. data/spec/selenium_spec_firefox.rb +208 -0
  304. data/spec/selenium_spec_firefox_remote.rb +80 -0
  305. data/spec/selenium_spec_ie.rb +150 -0
  306. data/spec/selenium_spec_safari.rb +148 -0
  307. data/spec/server_spec.rb +292 -0
  308. data/spec/session_spec.rb +91 -0
  309. data/spec/shared_selenium_node.rb +83 -0
  310. data/spec/shared_selenium_session.rb +476 -0
  311. data/spec/spec_helper.rb +100 -0
  312. data/spec/xpath_builder_spec.rb +93 -0
  313. metadata +753 -0
@@ -0,0 +1,196 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Capybara::Result do
6
+ let :string do
7
+ Capybara.string <<-STRING
8
+ <ul>
9
+ <li>Alpha</li>
10
+ <li>Beta</li>
11
+ <li>Gamma</li>
12
+ <li>Delta</li>
13
+ </ul>
14
+ STRING
15
+ end
16
+
17
+ let :result do
18
+ string.all '//li', minimum: 0 # pass minimum: 0 so lazy evaluation doesn't get triggered yet
19
+ end
20
+
21
+ it 'has a length' do
22
+ expect(result.length).to eq(4)
23
+ end
24
+
25
+ it 'has a first element' do
26
+ result.first.text == 'Alpha'
27
+ end
28
+
29
+ it 'has a last element' do
30
+ result.last.text == 'Delta'
31
+ end
32
+
33
+ it 'can supports values_at method' do
34
+ expect(result.values_at(0, 2).map(&:text)).to eq(%w[Alpha Gamma])
35
+ end
36
+
37
+ it 'can return an element by its index' do
38
+ expect(result.at(1).text).to eq('Beta')
39
+ expect(result[2].text).to eq('Gamma')
40
+ end
41
+
42
+ it 'can be mapped' do
43
+ expect(result.map(&:text)).to eq(%w[Alpha Beta Gamma Delta])
44
+ end
45
+
46
+ it 'can be selected' do
47
+ expect(result.count do |element|
48
+ element.text.include? 't'
49
+ end).to eq(2)
50
+ end
51
+
52
+ it 'can be reduced' do
53
+ expect(result.reduce('') do |memo, element|
54
+ memo + element.text[0]
55
+ end).to eq('ABGD')
56
+ end
57
+
58
+ it 'can be sampled' do
59
+ expect(result).to include(result.sample)
60
+ end
61
+
62
+ it 'can be indexed' do
63
+ expect(result.index do |el|
64
+ el.text == 'Gamma'
65
+ end).to eq(2)
66
+ end
67
+
68
+ def recalc_result
69
+ string.all '//li', minimum: 0 # pass minimum: 0 so lazy evaluation doesn't get triggered yet
70
+ end
71
+
72
+ it 'supports all modes of []' do
73
+ expect(recalc_result[1].text).to eq 'Beta'
74
+ expect(recalc_result[0, 2].map(&:text)).to eq %w[Alpha Beta]
75
+ expect(recalc_result[1..3].map(&:text)).to eq %w[Beta Gamma Delta]
76
+ expect(recalc_result[-1].text).to eq 'Delta'
77
+ expect(recalc_result[-2, 3].map(&:text)).to eq %w[Gamma Delta]
78
+ expect(recalc_result[1...3].map(&:text)).to eq %w[Beta Gamma]
79
+ expect(recalc_result[1..7].map(&:text)).to eq %w[Beta Gamma Delta]
80
+ expect(recalc_result[2...-1].map(&:text)).to eq %w[Gamma]
81
+ expect(recalc_result[2..-1].map(&:text)).to eq %w[Gamma Delta]
82
+ end
83
+
84
+ eval <<~TEST, binding, __FILE__, __LINE__ + 1 if RUBY_VERSION.to_f > 2.5
85
+ it 'supports endless ranges' do
86
+ expect(result[2..].map(&:text)).to eq %w[Gamma Delta]
87
+ end
88
+ TEST
89
+
90
+ eval <<~TEST, binding, __FILE__, __LINE__ + 1 if RUBY_VERSION.to_f > 2.6
91
+ it 'supports inclusive positive beginless ranges' do
92
+ expect(result[..2].map(&:text)).to eq %w[Alpha Beta Gamma]
93
+ end
94
+
95
+ it 'supports inclusive negative beginless ranges' do
96
+ expect(result[..-2].map(&:text)).to eq %w[Alpha Beta Gamma]
97
+ expect(result[..-1].map(&:text)).to eq %w[Alpha Beta Gamma Delta]
98
+ end
99
+
100
+ it 'supports exclusive positive beginless ranges' do
101
+ expect(result[...2].map(&:text)).to eq %w[Alpha Beta]
102
+ end
103
+
104
+ it 'supports exclusive negative beginless ranges' do
105
+ expect(result[...-2].map(&:text)).to eq %w[Alpha Beta]
106
+ expect(result[...-1].map(&:text)).to eq %w[Alpha Beta Gamma]
107
+ end
108
+ TEST
109
+
110
+ it 'works with filter blocks' do
111
+ result = string.all('//li') { |node| node.text == 'Alpha' }
112
+ expect(result.size).to eq 1
113
+ end
114
+
115
+ # Not a great test but it indirectly tests what is needed
116
+ it 'should evaluate filters lazily for idx' do
117
+ skip 'JRuby has an issue with lazy enumerator evaluation' if jruby_lazy_enumerator_workaround?
118
+ # Not processed until accessed
119
+ expect(result.instance_variable_get('@result_cache').size).to be 0
120
+
121
+ # Only one retrieved when needed
122
+ result.first
123
+ expect(result.instance_variable_get('@result_cache').size).to be 1
124
+
125
+ # works for indexed access
126
+ result[0]
127
+ expect(result.instance_variable_get('@result_cache').size).to be 1
128
+
129
+ result[2]
130
+ expect(result.instance_variable_get('@result_cache').size).to be 3
131
+
132
+ # All cached when converted to array
133
+ result.to_a
134
+ expect(result.instance_variable_get('@result_cache').size).to eq 4
135
+ end
136
+
137
+ it 'should evaluate filters lazily for range' do
138
+ skip 'JRuby has an issue with lazy enumerator evaluation' if jruby_lazy_enumerator_workaround?
139
+ result[0..1]
140
+ expect(result.instance_variable_get('@result_cache').size).to be 2
141
+
142
+ expect(result[0..7].size).to eq 4
143
+ expect(result.instance_variable_get('@result_cache').size).to be 4
144
+ end
145
+
146
+ it 'should evaluate filters lazily for idx and length' do
147
+ skip 'JRuby has an issue with lazy enumerator evaluation' if jruby_lazy_enumerator_workaround?
148
+ result[1, 2]
149
+ expect(result.instance_variable_get('@result_cache').size).to be 3
150
+
151
+ expect(result[2, 5].size).to eq 2
152
+ expect(result.instance_variable_get('@result_cache').size).to be 4
153
+ end
154
+
155
+ it 'should only need to evaluate one result for any?' do
156
+ skip 'JRuby has an issue with lazy enumerator evaluation' if jruby_lazy_enumerator_workaround?
157
+ result.any?
158
+ expect(result.instance_variable_get('@result_cache').size).to be 1
159
+ end
160
+
161
+ it 'should evaluate all elements when #to_a called' do
162
+ # All cached when converted to array
163
+ result.to_a
164
+ expect(result.instance_variable_get('@result_cache').size).to eq 4
165
+ end
166
+
167
+ describe '#each' do
168
+ it 'lazily evaluates' do
169
+ skip 'JRuby has an issue with lazy enumerator evaluation' if jruby_lazy_enumerator_workaround?
170
+ results = []
171
+ result.each do |el|
172
+ results << el
173
+ expect(result.instance_variable_get('@result_cache').size).to eq results.size
174
+ end
175
+
176
+ expect(results.size).to eq 4
177
+ end
178
+
179
+ context 'without a block' do
180
+ it 'returns an iterator' do
181
+ expect(result.each).to be_a(Enumerator)
182
+ end
183
+
184
+ it 'lazily evaluates' do
185
+ skip 'JRuby has an issue with lazy enumerator evaluation' if jruby_lazy_enumerator_workaround?
186
+ result.each.with_index do |_el, idx|
187
+ expect(result.instance_variable_get('@result_cache').size).to eq(idx + 1) # 0 indexing
188
+ end
189
+ end
190
+ end
191
+ end
192
+
193
+ def jruby_lazy_enumerator_workaround?
194
+ RUBY_PLATFORM == 'java'
195
+ end
196
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'capybara/rspec'
5
+
6
+ # rubocop:disable RSpec/InstanceVariable
7
+ RSpec.configuration.before(:each, file_path: './spec/rspec/features_spec.rb') do
8
+ @in_filtered_hook = true
9
+ end
10
+
11
+ feature "Capybara's feature DSL" do
12
+ background do
13
+ @in_background = true
14
+ end
15
+
16
+ scenario 'includes Capybara' do
17
+ visit('/')
18
+ expect(page).to have_content('Hello world!')
19
+ end
20
+
21
+ scenario 'preserves description' do |ex|
22
+ expect(ex.metadata[:full_description])
23
+ .to eq("Capybara's feature DSL preserves description")
24
+ end
25
+
26
+ scenario 'allows driver switching', driver: :selenium do
27
+ expect(Capybara.current_driver).to eq(:selenium)
28
+ end
29
+
30
+ scenario 'runs background' do
31
+ expect(@in_background).to be_truthy
32
+ end
33
+
34
+ scenario 'runs hooks filtered by file path' do
35
+ expect(@in_filtered_hook).to be_truthy
36
+ end
37
+
38
+ scenario "doesn't pollute the Object namespace" do
39
+ expect(Object.new).not_to respond_to(:feature)
40
+ end
41
+
42
+ feature 'nested features' do
43
+ scenario 'work as expected' do
44
+ visit '/'
45
+ expect(page).to have_content 'Hello world!'
46
+ end
47
+
48
+ scenario 'are marked in the metadata as capybara_feature' do |ex|
49
+ expect(ex.metadata[:capybara_feature]).to be_truthy
50
+ end
51
+
52
+ scenario 'have a type of :feature' do |ex|
53
+ expect(ex.metadata[:type]).to eq :feature
54
+ end
55
+ end
56
+ end
57
+ # rubocop:enable RSpec/InstanceVariable
58
+
59
+ feature 'given and given! aliases to let and let!' do
60
+ given(:value) { :available }
61
+ given!(:value_in_background) { :available }
62
+
63
+ background do
64
+ expect(value_in_background).to be(:available)
65
+ end
66
+
67
+ scenario 'given and given! work as intended' do
68
+ expect(value).to be(:available)
69
+ expect(value_in_background).to be(:available)
70
+ end
71
+ end
72
+
73
+ feature "Capybara's feature DSL with driver", driver: :culerity do
74
+ scenario 'switches driver' do
75
+ expect(Capybara.current_driver).to eq(:culerity)
76
+ end
77
+ end
78
+
79
+ # rubocop:disable RSpec/RepeatedExample
80
+ xfeature 'if xfeature aliases to pending then' do
81
+ scenario "this should be 'temporarily disabled with xfeature'" do
82
+ # dummy
83
+ end
84
+
85
+ scenario "this also should be 'temporarily disabled with xfeature'" do
86
+ # dummy
87
+ end
88
+ end
89
+
90
+ ffeature 'if ffeature aliases focused tag then' do # rubocop:disable RSpec/Focus
91
+ scenario 'scenario inside this feature has metatag focus tag' do |example|
92
+ expect(example.metadata[:focus]).to eq true
93
+ end
94
+
95
+ scenario 'other scenarios also has metatag focus tag ' do |example|
96
+ expect(example.metadata[:focus]).to eq true
97
+ end
98
+ end
99
+ # rubocop:enable RSpec/RepeatedExample
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'capybara/rspec'
5
+
6
+ RSpec.configuration.before(:each, file_path: './spec/rspec/scenarios_spec.rb') do
7
+ @in_filtered_hook = true
8
+ end
9
+
10
+ feature 'if fscenario aliases focused tag then' do
11
+ fscenario 'scenario should have focused meta tag' do |example| # rubocop:disable RSpec/Focus
12
+ expect(example.metadata[:focus]).to eq true
13
+ end
14
+ end
15
+
16
+ feature 'if xscenario aliases to pending then' do
17
+ xscenario "this test should be 'temporarily disabled with xscenario'" do
18
+ end
19
+ end
@@ -0,0 +1,947 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'capybara/dsl'
5
+ require 'capybara/rspec/matchers'
6
+ require 'benchmark'
7
+
8
+ # rubocop:disable RSpec/ExpectActual
9
+
10
+ RSpec.shared_examples Capybara::RSpecMatchers do |session, _mode|
11
+ include Capybara::DSL
12
+ include Capybara::RSpecMatchers
13
+
14
+ describe 'have_css matcher' do
15
+ it 'gives proper description' do
16
+ expect(have_css('h1').description).to eq('have visible css "h1"')
17
+ end
18
+
19
+ context 'on a string' do
20
+ context 'with should' do
21
+ it 'passes if has_css? returns true' do
22
+ expect('<h1>Text</h1>').to have_css('h1')
23
+ end
24
+
25
+ it 'fails if has_css? returns false' do
26
+ expect do
27
+ expect('<h1>Text</h1>').to have_css('h2')
28
+ end.to raise_error(/expected to find css "h2" but there were no matches/)
29
+ end
30
+
31
+ it 'passes if matched node count equals expected count' do
32
+ expect('<h1>Text</h1>').to have_css('h1', count: 1)
33
+ end
34
+
35
+ it 'fails if matched node count does not equal expected count' do
36
+ expect do
37
+ expect('<h1>Text</h1>').to have_css('h1', count: 2)
38
+ end.to raise_error('expected to find visible css "h1" 2 times, found 1 match: "Text"')
39
+ end
40
+
41
+ it 'fails if matched node count is less than expected minimum count' do
42
+ expect do
43
+ expect('<h1>Text</h1>').to have_css('p', minimum: 1)
44
+ end.to raise_error('expected to find css "p" at least 1 time but there were no matches')
45
+ end
46
+
47
+ it 'fails if matched node count is more than expected maximum count' do
48
+ expect do
49
+ expect('<h1>Text</h1><h1>Text</h1><h1>Text</h1>').to have_css('h1', maximum: 2)
50
+ end.to raise_error('expected to find visible css "h1" at most 2 times, found 3 matches: "Text", "Text", "Text"')
51
+ end
52
+
53
+ it 'fails if matched node count does not belong to expected range' do
54
+ expect do
55
+ expect('<h1>Text</h1>').to have_css('h1', between: 2..3)
56
+ end.to raise_error('expected to find visible css "h1" between 2 and 3 times, found 1 match: "Text"')
57
+ end
58
+ end
59
+
60
+ context 'with should_not' do
61
+ it 'passes if has_no_css? returns true' do
62
+ expect('<h1>Text</h1>').not_to have_css('h2')
63
+ end
64
+
65
+ it 'fails if has_no_css? returns false' do
66
+ expect do
67
+ expect('<h1>Text</h1>').not_to have_css('h1')
68
+ end.to raise_error(/expected not to find visible css "h1"/)
69
+ end
70
+
71
+ it 'passes if matched node count does not equal expected count' do
72
+ expect('<h1>Text</h1>').not_to have_css('h1', count: 2)
73
+ end
74
+
75
+ it 'fails if matched node count equals expected count' do
76
+ expect do
77
+ expect('<h1>Text</h1>').not_to have_css('h1', count: 1)
78
+ end.to raise_error(/expected not to find visible css "h1"/)
79
+ end
80
+ end
81
+
82
+ it 'supports compounding' do
83
+ expect('<h1>Text</h1><h2>Text</h2>').to have_css('h1').and have_css('h2')
84
+ expect('<h1>Text</h1><h2>Text</h2>').to have_css('h3').or have_css('h1')
85
+ expect('<h1>Text</h1><h2>Text</h2>').to have_no_css('h4').and have_css('h2')
86
+ expect('<h1>Text</h1><h2>Text</h2>').to have_no_css('h2').or have_css('h1')
87
+ end
88
+ end
89
+
90
+ context 'on a page or node' do
91
+ before do
92
+ visit('/with_html')
93
+ end
94
+
95
+ context 'with should' do
96
+ it 'passes if has_css? returns true' do
97
+ expect(page).to have_css('h1')
98
+ end
99
+
100
+ it 'fails if has_css? returns false' do
101
+ expect do
102
+ expect(page).to have_css('h1#doesnotexist')
103
+ end.to raise_error(/expected to find css "h1#doesnotexist" but there were no matches/)
104
+ end
105
+ end
106
+
107
+ context 'with should_not' do
108
+ it 'passes if has_no_css? returns true' do
109
+ expect(page).not_to have_css('h1#doesnotexist')
110
+ end
111
+
112
+ it 'fails if has_no_css? returns false' do
113
+ expect do
114
+ expect(page).not_to have_css('h1')
115
+ end.to raise_error(/expected not to find visible css "h1"/)
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ describe 'have_xpath matcher' do
122
+ it 'gives proper description' do
123
+ expect(have_xpath('//h1').description).to eq("have visible xpath \"\/\/h1\"")
124
+ end
125
+
126
+ context 'on a string' do
127
+ context 'with should' do
128
+ it 'passes if has_xpath? returns true' do
129
+ expect('<h1>Text</h1>').to have_xpath('//h1')
130
+ end
131
+
132
+ it 'fails if has_xpath? returns false' do
133
+ expect do
134
+ expect('<h1>Text</h1>').to have_xpath('//h2')
135
+ end.to raise_error(%r{expected to find xpath "//h2" but there were no matches})
136
+ end
137
+ end
138
+
139
+ context 'with should_not' do
140
+ it 'passes if has_no_xpath? returns true' do
141
+ expect('<h1>Text</h1>').not_to have_xpath('//h2')
142
+ end
143
+
144
+ it 'fails if has_no_xpath? returns false' do
145
+ expect do
146
+ expect('<h1>Text</h1>').not_to have_xpath('//h1')
147
+ end.to raise_error(%r{expected not to find visible xpath "//h1"})
148
+ end
149
+ end
150
+
151
+ it 'supports compounding' do
152
+ expect('<h1>Text</h1><h2>Text</h2>').to have_xpath('//h1').and have_xpath('//h2')
153
+ expect('<h1>Text</h1><h2>Text</h2>').to have_xpath('//h3').or have_xpath('//h1')
154
+ expect('<h1>Text</h1><h2>Text</h2>').to have_no_xpath('//h4').and have_xpath('//h1')
155
+ expect('<h1>Text</h1><h2>Text</h2>').to have_no_xpath('//h4').or have_xpath('//h4')
156
+ end
157
+ end
158
+
159
+ context 'on a page or node' do
160
+ before do
161
+ visit('/with_html')
162
+ end
163
+
164
+ context 'with should' do
165
+ it 'passes if has_xpath? returns true' do
166
+ expect(page).to have_xpath('//h1')
167
+ end
168
+
169
+ it 'fails if has_xpath? returns false' do
170
+ expect do
171
+ expect(page).to have_xpath("//h1[@id='doesnotexist']")
172
+ end.to raise_error(%r{expected to find xpath "//h1\[@id='doesnotexist'\]" but there were no matches})
173
+ end
174
+ end
175
+
176
+ context 'with should_not' do
177
+ it 'passes if has_no_xpath? returns true' do
178
+ expect(page).not_to have_xpath('//h1[@id="doesnotexist"]')
179
+ end
180
+
181
+ it 'fails if has_no_xpath? returns false' do
182
+ expect do
183
+ expect(page).not_to have_xpath('//h1')
184
+ end.to raise_error(%r{expected not to find visible xpath "//h1"})
185
+ end
186
+ end
187
+ end
188
+ end
189
+
190
+ describe 'have_selector matcher' do
191
+ it 'gives proper description' do
192
+ matcher = have_selector('//h1')
193
+ expect('<h1>Text</h1>').to matcher
194
+ expect(matcher.description).to eq('have visible xpath "//h1"')
195
+ end
196
+
197
+ context 'on a string' do
198
+ context 'with should' do
199
+ it 'passes if has_selector? returns true' do
200
+ expect('<h1>Text</h1>').to have_selector('//h1')
201
+ end
202
+
203
+ it 'fails if has_selector? returns false' do
204
+ expect do
205
+ expect('<h1>Text</h1>').to have_selector('//h2')
206
+ end.to raise_error(%r{expected to find xpath "//h2" but there were no matches})
207
+ end
208
+ end
209
+
210
+ context 'with should_not' do
211
+ it 'passes if has_no_selector? returns true' do
212
+ expect('<h1>Text</h1>').not_to have_selector(:css, 'h2')
213
+ end
214
+
215
+ it 'fails if has_no_selector? returns false' do
216
+ expect do
217
+ expect('<h1>Text</h1>').not_to have_selector(:css, 'h1')
218
+ end.to raise_error(/expected not to find visible css "h1"/)
219
+ end
220
+ end
221
+ end
222
+
223
+ context 'on a page or node' do
224
+ before do
225
+ visit('/with_html')
226
+ end
227
+
228
+ context 'with should' do
229
+ it 'passes if has_selector? returns true' do
230
+ expect(page).to have_selector('//h1', text: 'test')
231
+ end
232
+
233
+ it 'fails if has_selector? returns false' do
234
+ expect do
235
+ expect(page).to have_selector("//h1[@id='doesnotexist']")
236
+ end.to raise_error(%r{expected to find xpath "//h1\[@id='doesnotexist'\]" but there were no matches})
237
+ end
238
+
239
+ it 'includes text in error message' do
240
+ expect do
241
+ expect(page).to have_selector('//h1', text: 'wrong text')
242
+ end.to raise_error(%r{expected to find visible xpath "//h1" with text "wrong text" but there were no matches})
243
+ end
244
+ end
245
+
246
+ context 'with should_not' do
247
+ it 'passes if has_no_css? returns true' do
248
+ expect(page).not_to have_selector(:css, 'h1#doesnotexist')
249
+ end
250
+
251
+ it 'fails if has_no_selector? returns false' do
252
+ expect do
253
+ expect(page).not_to have_selector(:css, 'h1', text: 'test')
254
+ end.to raise_error(/expected not to find visible css "h1" with text "test"/)
255
+ end
256
+ end
257
+ end
258
+
259
+ it 'supports compounding' do
260
+ expect('<h1>Text</h1><h2>Text</h2>').to have_selector('//h1').and have_selector('//h2')
261
+ expect('<h1>Text</h1><h2>Text</h2>').to have_selector('//h3').or have_selector('//h1')
262
+ expect('<h1>Text</h1><h2>Text</h2>').to have_no_selector('//h3').and have_selector('//h1')
263
+ end
264
+ end
265
+
266
+ describe 'have_content matcher' do
267
+ it 'gives proper description' do
268
+ expect(have_content('Text').description).to eq('have text "Text"')
269
+ end
270
+
271
+ context 'on a string' do
272
+ context 'with should' do
273
+ it 'passes if has_content? returns true' do
274
+ expect('<h1>Text</h1>').to have_content('Text')
275
+ end
276
+
277
+ it 'passes if has_content? returns true using regexp' do
278
+ expect('<h1>Text</h1>').to have_content(/ext/)
279
+ end
280
+
281
+ it 'fails if has_content? returns false' do
282
+ expect do
283
+ expect('<h1>Text</h1>').to have_content('No such Text')
284
+ end.to raise_error(/expected to find text "No such Text" in "Text"/)
285
+ end
286
+ end
287
+
288
+ context 'with should_not' do
289
+ it 'passes if has_no_content? returns true' do
290
+ expect('<h1>Text</h1>').not_to have_content('No such Text')
291
+ end
292
+
293
+ it 'passes because escapes any characters that would have special meaning in a regexp' do
294
+ expect('<h1>Text</h1>').not_to have_content('.')
295
+ end
296
+
297
+ it 'fails if has_no_content? returns false' do
298
+ expect do
299
+ expect('<h1>Text</h1>').not_to have_content('Text')
300
+ end.to raise_error(/expected not to find text "Text" in "Text"/)
301
+ end
302
+ end
303
+ end
304
+
305
+ context 'on a page or node' do
306
+ before do
307
+ visit('/with_html')
308
+ end
309
+
310
+ context 'with should' do
311
+ it 'passes if has_content? returns true' do
312
+ expect(page).to have_content('This is a test')
313
+ end
314
+
315
+ it 'passes if has_content? returns true using regexp' do
316
+ expect(page).to have_content(/test/)
317
+ end
318
+
319
+ it 'fails if has_content? returns false' do
320
+ expect do
321
+ expect(page).to have_content('No such Text')
322
+ end.to raise_error(/expected to find text "No such Text" in "(.*)This is a test(.*)"/)
323
+ end
324
+
325
+ context 'with default selector CSS' do
326
+ before { Capybara.default_selector = :css }
327
+
328
+ after { Capybara.default_selector = :xpath }
329
+
330
+ it 'fails if has_content? returns false' do
331
+ expect do
332
+ expect(page).to have_content('No such Text')
333
+ end.to raise_error(/expected to find text "No such Text" in "(.*)This is a test(.*)"/)
334
+ end
335
+ end
336
+ end
337
+
338
+ context 'with should_not' do
339
+ it 'passes if has_no_content? returns true' do
340
+ expect(page).not_to have_content('No such Text')
341
+ end
342
+
343
+ it 'fails if has_no_content? returns false' do
344
+ expect do
345
+ expect(page).not_to have_content('This is a test')
346
+ end.to raise_error(/expected not to find text "This is a test"/)
347
+ end
348
+ end
349
+ end
350
+
351
+ it 'supports compounding' do
352
+ expect('<h1>Text</h1><h2>And</h2>').to have_content('Text').and have_content('And')
353
+ expect('<h1>Text</h1><h2>Or</h2>').to have_content('XYZ').or have_content('Or')
354
+ expect('<h1>Text</h1><h2>Or</h2>').to have_no_content('XYZ').and have_content('Or')
355
+ end
356
+ end
357
+
358
+ describe 'have_text matcher' do
359
+ it 'gives proper description' do
360
+ expect(have_text('Text').description).to eq('have text "Text"')
361
+ end
362
+
363
+ context 'on a string' do
364
+ context 'with should' do
365
+ it 'passes if text contains given string' do
366
+ expect('<h1>Text</h1>').to have_text('Text')
367
+ end
368
+
369
+ it 'passes if text matches given regexp' do
370
+ expect('<h1>Text</h1>').to have_text(/ext/)
371
+ end
372
+
373
+ it "fails if text doesn't contain given string" do
374
+ expect do
375
+ expect('<h1>Text</h1>').to have_text('No such Text')
376
+ end.to raise_error(/expected to find text "No such Text" in "Text"/)
377
+ end
378
+
379
+ it "fails if text doesn't match given regexp" do
380
+ expect do
381
+ expect('<h1>Text</h1>').to have_text(/No such Text/)
382
+ end.to raise_error('expected to find text matching /No such Text/ in "Text"')
383
+ end
384
+
385
+ it 'casts Integer to string' do
386
+ expect do
387
+ expect('<h1>Text</h1>').to have_text(3)
388
+ end.to raise_error(/expected to find text "3" in "Text"/)
389
+ end
390
+
391
+ it 'fails if matched text count does not equal to expected count' do
392
+ expect do
393
+ expect('<h1>Text</h1>').to have_text('Text', count: 2)
394
+ end.to raise_error('expected to find text "Text" 2 times but found 1 time in "Text"')
395
+ end
396
+
397
+ it 'fails if matched text count is less than expected minimum count' do
398
+ expect do
399
+ expect('<h1>Text</h1>').to have_text('Lorem', minimum: 1)
400
+ end.to raise_error('expected to find text "Lorem" at least 1 time but found 0 times in "Text"')
401
+ end
402
+
403
+ it 'fails if matched text count is more than expected maximum count' do
404
+ expect do
405
+ expect('<h1>Text TextText</h1>').to have_text('Text', maximum: 2)
406
+ end.to raise_error('expected to find text "Text" at most 2 times but found 3 times in "Text TextText"')
407
+ end
408
+
409
+ it 'fails if matched text count does not belong to expected range' do
410
+ expect do
411
+ expect('<h1>Text</h1>').to have_text('Text', between: 2..3)
412
+ end.to raise_error('expected to find text "Text" between 2 and 3 times but found 1 time in "Text"')
413
+ end
414
+ end
415
+
416
+ context 'with should_not' do
417
+ it "passes if text doesn't contain a string" do
418
+ expect('<h1>Text</h1>').not_to have_text('No such Text')
419
+ end
420
+
421
+ it 'passes because escapes any characters that would have special meaning in a regexp' do
422
+ expect('<h1>Text</h1>').not_to have_text('.')
423
+ end
424
+
425
+ it 'fails if text contains a string' do
426
+ expect do
427
+ expect('<h1>Text</h1>').not_to have_text('Text')
428
+ end.to raise_error(/expected not to find text "Text" in "Text"/)
429
+ end
430
+ end
431
+ end
432
+
433
+ context 'on a page or node' do
434
+ before do
435
+ visit('/with_html')
436
+ end
437
+
438
+ context 'with should' do
439
+ it 'passes if has_text? returns true' do
440
+ expect(page).to have_text('This is a test')
441
+ end
442
+
443
+ it 'passes if has_text? returns true using regexp' do
444
+ expect(page).to have_text(/test/)
445
+ end
446
+
447
+ it 'can check for all text' do
448
+ expect(page).to have_text(:all, 'Some of this text is hidden!')
449
+ end
450
+
451
+ it 'can check for visible text' do
452
+ expect(page).to have_text(:visible, 'Some of this text is')
453
+ expect(page).not_to have_text(:visible, 'Some of this text is hidden!')
454
+ end
455
+
456
+ it 'fails if has_text? returns false' do
457
+ expect do
458
+ expect(page).to have_text('No such Text')
459
+ end.to raise_error(/expected to find text "No such Text" in "(.*)This is a test(.*)"/)
460
+ end
461
+
462
+ context 'with default selector CSS' do
463
+ before { Capybara.default_selector = :css }
464
+
465
+ after { Capybara.default_selector = :xpath }
466
+
467
+ it 'fails if has_text? returns false' do
468
+ expect do
469
+ expect(page).to have_text('No such Text')
470
+ end.to raise_error(/expected to find text "No such Text" in "(.*)This is a test(.*)"/)
471
+ end
472
+ end
473
+ end
474
+
475
+ context 'with should_not' do
476
+ it 'passes if has_no_text? returns true' do
477
+ expect(page).not_to have_text('No such Text')
478
+ end
479
+
480
+ it 'fails if has_no_text? returns false' do
481
+ expect do
482
+ expect(page).not_to have_text('This is a test')
483
+ end.to raise_error(/expected not to find text "This is a test"/)
484
+ end
485
+ end
486
+ end
487
+
488
+ it 'supports compounding' do
489
+ expect('<h1>Text</h1><h2>And</h2>').to have_text('Text').and have_text('And')
490
+ expect('<h1>Text</h1><h2>Or</h2>').to have_text('Not here').or have_text('Or')
491
+ end
492
+ end
493
+
494
+ describe 'have_link matcher' do
495
+ let(:html) { '<a href="#">Just a link</a><a href="#">Another link</a>' }
496
+
497
+ it 'gives proper description' do
498
+ expect(have_link('Just a link').description).to eq('have visible link "Just a link"')
499
+ end
500
+
501
+ it 'passes if there is such a link' do
502
+ expect(html).to have_link('Just a link')
503
+ end
504
+
505
+ it 'fails if there is no such link' do
506
+ expect do
507
+ expect(html).to have_link('No such Link')
508
+ end.to raise_error(/expected to find link "No such Link"/)
509
+ end
510
+
511
+ it 'supports compounding' do
512
+ expect(html).to have_link('Just a link').and have_link('Another link')
513
+ expect(html).to have_link('Not a link').or have_link('Another link')
514
+ expect(html).to have_no_link('Not a link').and have_link('Another link')
515
+ end
516
+ end
517
+
518
+ describe 'have_title matcher' do
519
+ it 'gives proper description' do
520
+ expect(have_title('Just a title').description).to eq('have title "Just a title"')
521
+ end
522
+
523
+ context 'on a string' do
524
+ let(:html) { '<title>Just a title</title>' }
525
+
526
+ it 'passes if there is such a title' do
527
+ expect(html).to have_title('Just a title')
528
+ end
529
+
530
+ it 'fails if there is no such title' do
531
+ expect do
532
+ expect(html).to have_title('No such title')
533
+ end.to raise_error('expected "Just a title" to include "No such title"')
534
+ end
535
+
536
+ it "fails if title doesn't match regexp" do
537
+ expect do
538
+ expect(html).to have_title(/[[:upper:]]+[[:lower:]]+l{2}o/)
539
+ end.to raise_error('expected "Just a title" to match /[[:upper:]]+[[:lower:]]+l{2}o/')
540
+ end
541
+ end
542
+
543
+ context 'on a page or node' do
544
+ it 'passes if there is such a title' do
545
+ visit('/with_js')
546
+ expect(page).to have_title('with_js')
547
+ end
548
+
549
+ it 'fails if there is no such title' do
550
+ visit('/with_js')
551
+ expect do
552
+ expect(page).to have_title('No such title')
553
+ end.to raise_error('expected "with_js" to include "No such title"')
554
+ end
555
+
556
+ context 'with wait' do
557
+ before do
558
+ session.visit('/with_js')
559
+ end
560
+
561
+ it 'waits if wait time is more than timeout' do
562
+ session.click_link('Change title')
563
+ using_wait_time 0 do
564
+ expect(session).to have_title('changed title', wait: 2)
565
+ end
566
+ end
567
+
568
+ it "doesn't wait if wait time is less than timeout" do
569
+ session.click_link('Change title')
570
+ using_wait_time 3 do
571
+ expect(session).not_to have_title('changed title', wait: 0)
572
+ end
573
+ end
574
+ end
575
+ end
576
+
577
+ it 'supports compounding' do
578
+ expect('<title>I compound</title>').to have_title('I dont compound').or have_title('I compound')
579
+ end
580
+ end
581
+
582
+ describe 'have_current_path matcher' do
583
+ it 'gives proper description' do
584
+ expect(have_current_path('http://www.example.com').description).to eq('have current path "http://www.example.com"')
585
+ end
586
+
587
+ context 'on a page or node' do
588
+ it 'passes if there is such a current path' do
589
+ visit('/with_js')
590
+ expect(page).to have_current_path('/with_js')
591
+ end
592
+
593
+ it 'fails if there is no such current_path' do
594
+ visit('/with_js')
595
+ expect do
596
+ expect(page).to have_current_path('/not_with_js')
597
+ end.to raise_error('expected "/with_js" to equal "/not_with_js"')
598
+ end
599
+
600
+ context 'with wait' do
601
+ before do
602
+ session.visit('/with_js')
603
+ end
604
+
605
+ it 'waits if wait time is more than timeout' do
606
+ session.click_link('Change page')
607
+ using_wait_time 0 do
608
+ expect(session).to have_current_path('/with_html', wait: 2)
609
+ end
610
+ end
611
+
612
+ it "doesn't wait if wait time is less than timeout" do
613
+ session.click_link('Change page')
614
+ using_wait_time 0 do
615
+ expect(session).not_to have_current_path('/with_html')
616
+ end
617
+ end
618
+ end
619
+ end
620
+
621
+ it 'supports compounding' do
622
+ visit('/with_html')
623
+ expect(page).to have_current_path('/not_with_html').or have_current_path('/with_html')
624
+ end
625
+ end
626
+
627
+ describe 'have_button matcher' do
628
+ let(:html) { '<button>A button</button><input type="submit" value="Another button"/>' }
629
+
630
+ it 'gives proper description with no options' do
631
+ expect(have_button('A button').description).to eq('have visible button "A button" that is not disabled')
632
+ end
633
+
634
+ it 'gives proper description with disabled :any option' do
635
+ expect(have_button('A button', disabled: :all).description).to eq('have visible button "A button"')
636
+ end
637
+
638
+ it 'passes if there is such a button' do
639
+ expect(html).to have_button('A button')
640
+ end
641
+
642
+ it 'fails if there is no such button' do
643
+ expect do
644
+ expect(html).to have_button('No such Button')
645
+ end.to raise_error(/expected to find button "No such Button"/)
646
+ end
647
+
648
+ it 'supports compounding' do
649
+ expect(html).to have_button('Not this button').or have_button('A button')
650
+ end
651
+ end
652
+
653
+ describe 'have_field matcher' do
654
+ let(:html) { '<p><label>Text field<input type="text" value="some value"/></label></p>' }
655
+
656
+ it 'gives proper description' do
657
+ expect(have_field('Text field').description).to eq('have visible field "Text field" that is not disabled')
658
+ end
659
+
660
+ it 'gives proper description for a given value' do
661
+ expect(have_field('Text field', with: 'some value').description).to eq('have visible field "Text field" that is not disabled with value "some value"')
662
+ end
663
+
664
+ it 'passes if there is such a field' do
665
+ expect(html).to have_field('Text field')
666
+ end
667
+
668
+ it 'passes if there is such a field with value' do
669
+ expect(html).to have_field('Text field', with: 'some value')
670
+ end
671
+
672
+ it 'fails if there is no such field' do
673
+ expect do
674
+ expect(html).to have_field('No such Field')
675
+ end.to raise_error(/expected to find field "No such Field"/)
676
+ end
677
+
678
+ it 'fails if there is such field but with false value' do
679
+ expect do
680
+ expect(html).to have_field('Text field', with: 'false value')
681
+ end.to raise_error(/expected to find visible field "Text field"/)
682
+ end
683
+
684
+ it 'treats a given value as a string' do
685
+ foo = Class.new do
686
+ def to_s
687
+ 'some value'
688
+ end
689
+ end
690
+ expect(html).to have_field('Text field', with: foo.new)
691
+ end
692
+
693
+ it 'supports compounding' do
694
+ expect(html).to have_field('Not this one').or have_field('Text field')
695
+ end
696
+ end
697
+
698
+ describe 'have_checked_field matcher' do
699
+ let(:html) do
700
+ '<label>it is checked<input type="checkbox" checked="checked"/></label>
701
+ <label>unchecked field<input type="checkbox"/></label>'
702
+ end
703
+
704
+ it 'gives proper description' do
705
+ expect(have_checked_field('it is checked').description).to eq('have visible field "it is checked" that is not disabled that is checked')
706
+ end
707
+
708
+ context 'with should' do
709
+ it 'passes if there is such a field and it is checked' do
710
+ expect(html).to have_checked_field('it is checked')
711
+ end
712
+
713
+ it 'fails if there is such a field but it is not checked' do
714
+ expect do
715
+ expect(html).to have_checked_field('unchecked field')
716
+ end.to raise_error(/expected to find visible field "unchecked field"/)
717
+ end
718
+
719
+ it 'fails if there is no such field' do
720
+ expect do
721
+ expect(html).to have_checked_field('no such field')
722
+ end.to raise_error(/expected to find field "no such field"/)
723
+ end
724
+ end
725
+
726
+ context 'with should not' do
727
+ it 'fails if there is such a field and it is checked' do
728
+ expect do
729
+ expect(html).not_to have_checked_field('it is checked')
730
+ end.to raise_error(/expected not to find visible field "it is checked"/)
731
+ end
732
+
733
+ it 'passes if there is such a field but it is not checked' do
734
+ expect(html).not_to have_checked_field('unchecked field')
735
+ end
736
+
737
+ it 'passes if there is no such field' do
738
+ expect(html).not_to have_checked_field('no such field')
739
+ end
740
+ end
741
+
742
+ it 'supports compounding' do
743
+ expect(html).to have_checked_field('not this one').or have_checked_field('it is checked')
744
+ end
745
+ end
746
+
747
+ describe 'have_unchecked_field matcher' do
748
+ let(:html) do
749
+ '<label>it is checked<input type="checkbox" checked="checked"/></label>
750
+ <label>unchecked field<input type="checkbox"/></label>'
751
+ end
752
+
753
+ it 'gives proper description' do
754
+ expect(have_unchecked_field('unchecked field').description).to eq('have visible field "unchecked field" that is not disabled that is not checked')
755
+ end
756
+
757
+ context 'with should' do
758
+ it 'passes if there is such a field and it is not checked' do
759
+ expect(html).to have_unchecked_field('unchecked field')
760
+ end
761
+
762
+ it 'fails if there is such a field but it is checked' do
763
+ expect do
764
+ expect(html).to have_unchecked_field('it is checked')
765
+ end.to raise_error(/expected to find visible field "it is checked"/)
766
+ end
767
+
768
+ it 'fails if there is no such field' do
769
+ expect do
770
+ expect(html).to have_unchecked_field('no such field')
771
+ end.to raise_error(/expected to find field "no such field"/)
772
+ end
773
+ end
774
+
775
+ context 'with should not' do
776
+ it 'fails if there is such a field and it is not checked' do
777
+ expect do
778
+ expect(html).not_to have_unchecked_field('unchecked field')
779
+ end.to raise_error(/expected not to find visible field "unchecked field"/)
780
+ end
781
+
782
+ it 'passes if there is such a field but it is checked' do
783
+ expect(html).not_to have_unchecked_field('it is checked')
784
+ end
785
+
786
+ it 'passes if there is no such field' do
787
+ expect(html).not_to have_unchecked_field('no such field')
788
+ end
789
+ end
790
+
791
+ it 'supports compounding' do
792
+ expect(html).to have_unchecked_field('it is checked').or have_unchecked_field('unchecked field')
793
+ end
794
+ end
795
+
796
+ describe 'have_select matcher' do
797
+ let(:html) { '<label>Select Box<select></select></label>' }
798
+
799
+ it 'gives proper description' do
800
+ expect(have_select('Select Box').description).to eq('have visible select box "Select Box" that is not disabled')
801
+ end
802
+
803
+ it 'gives proper description for a given selected value' do
804
+ expect(have_select('Select Box', selected: 'some value').description).to eq('have visible select box "Select Box" that is not disabled with "some value" selected')
805
+ end
806
+
807
+ it 'passes if there is such a select' do
808
+ expect(html).to have_select('Select Box')
809
+ end
810
+
811
+ it 'fails if there is no such select' do
812
+ expect do
813
+ expect(html).to have_select('No such Select box')
814
+ end.to raise_error(/expected to find select box "No such Select box"/)
815
+ end
816
+
817
+ it 'supports compounding' do
818
+ expect(html).to have_select('Not this one').or have_select('Select Box')
819
+ end
820
+ end
821
+
822
+ describe 'have_table matcher' do
823
+ let(:html) { '<table><caption>Lovely table</caption></table>' }
824
+
825
+ it 'gives proper description' do
826
+ expect(have_table('Lovely table').description).to eq('have visible table "Lovely table"')
827
+ expect(have_table('Lovely table', caption: 'my caption').description).to eq('have visible table "Lovely table" with caption "my caption"')
828
+ end
829
+
830
+ it 'gives proper description when :visible option passed' do
831
+ expect(have_table('Lovely table', visible: true).description).to eq('have visible table "Lovely table"')
832
+ expect(have_table('Lovely table', visible: :hidden).description).to eq('have non-visible table "Lovely table"')
833
+ expect(have_table('Lovely table', visible: :all).description).to eq('have table "Lovely table"')
834
+ expect(have_table('Lovely table', visible: false).description).to eq('have table "Lovely table"')
835
+ end
836
+
837
+ it 'passes if there is such a table' do
838
+ expect(html).to have_table('Lovely table')
839
+ end
840
+
841
+ it 'fails if there is no such table' do
842
+ expect do
843
+ expect(html).to have_table('No such Table')
844
+ end.to raise_error(/expected to find table "No such Table"/)
845
+ end
846
+
847
+ it 'supports compounding' do
848
+ expect(html).to have_table('nope').or have_table('Lovely table')
849
+ end
850
+ end
851
+
852
+ context 'compounding timing' do
853
+ let(:session) { session }
854
+ let(:el) { session.find(:css, '#reload-me') }
855
+
856
+ before do
857
+ session.visit('/with_js')
858
+ end
859
+
860
+ describe '#and' do
861
+ it "should run 'concurrently'" do
862
+ Capybara.using_wait_time(2) do
863
+ matcher = have_text('this is not there').and have_text('neither is this')
864
+ expect(Benchmark.realtime do
865
+ expect do
866
+ expect(el).to matcher
867
+ end.to raise_error RSpec::Expectations::ExpectationNotMetError
868
+ end).to be_between(2, 3)
869
+ end
870
+ end
871
+
872
+ it "should run 'concurrently' and retry" do
873
+ session.click_link('reload-link')
874
+ session.using_wait_time(2) do
875
+ expect(Benchmark.realtime do
876
+ expect do
877
+ expect(el).to have_text('waiting to be reloaded').and(have_text('has been reloaded'))
878
+ end.to raise_error RSpec::Expectations::ExpectationNotMetError, /expected to find text "waiting to be reloaded" in "has been reloaded"/
879
+ end).to be_between(2, 3)
880
+ end
881
+ end
882
+
883
+ it 'should ignore :wait options' do
884
+ session.using_wait_time(2) do
885
+ matcher = have_text('this is not there', wait: 5).and have_text('neither is this', wait: 6)
886
+ expect(Benchmark.realtime do
887
+ expect do
888
+ expect(el).to matcher
889
+ end.to raise_error RSpec::Expectations::ExpectationNotMetError
890
+ end).to be_between(2, 3)
891
+ end
892
+ end
893
+
894
+ it 'should work on the session' do
895
+ session.using_wait_time(2) do
896
+ session.click_link('reload-link')
897
+ expect(session).to have_selector(:css, 'h1', text: 'FooBar').and have_text('has been reloaded')
898
+ end
899
+ end
900
+ end
901
+
902
+ describe '#and_then' do
903
+ it 'should run sequentially' do
904
+ session.click_link('reload-link')
905
+ expect(el).to have_text('waiting to be reloaded').and_then have_text('has been reloaded')
906
+ end
907
+ end
908
+
909
+ describe '#or' do
910
+ it "should run 'concurrently'" do
911
+ session.using_wait_time(3) do
912
+ expect(Benchmark.realtime do
913
+ expect(el).to have_text('has been reloaded').or have_text('waiting to be reloaded')
914
+ end).to be < 1
915
+ end
916
+ end
917
+
918
+ it 'should retry' do
919
+ session.using_wait_time(3) do
920
+ expect(Benchmark.realtime do
921
+ expect do
922
+ expect(el).to have_text('has been reloaded').or have_text('random stuff')
923
+ end.to raise_error RSpec::Expectations::ExpectationNotMetError
924
+ end).to be > 3
925
+ end
926
+ end
927
+
928
+ it 'should ignore :wait options' do
929
+ session.using_wait_time(2) do
930
+ expect(Benchmark.realtime do
931
+ expect do
932
+ expect(el).to have_text('this is not there', wait: 10).or have_text('neither is this', wait: 15)
933
+ end.to raise_error RSpec::Expectations::ExpectationNotMetError
934
+ end).to be_between(2, 3)
935
+ end
936
+ end
937
+
938
+ it 'should work on the session' do
939
+ session.using_wait_time(2) do
940
+ session.click_link('reload-link')
941
+ expect(session).to have_selector(:css, 'h1', text: 'Not on the page').or have_text('has been reloaded')
942
+ end
943
+ end
944
+ end
945
+ end
946
+ end
947
+ # rubocop:enable RSpec/ExpectActual