capybara 3.32.2

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