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,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Note: This file uses `sleep` to sync up parts of the tests. This is only implemented like this
4
+ # because of the methods being tested. In tests using Capybara this type of behavior should be implemented
5
+ # using Capybara provided assertions with builtin waiting behavior.
6
+
7
+ Capybara::SpecHelper.spec '#windows', requires: [:windows] do
8
+ before do
9
+ @window = @session.current_window
10
+ @session.visit('/with_windows')
11
+ @session.find(:css, '#openTwoWindows').click
12
+
13
+ @session.document.synchronize(3, errors: [Capybara::CapybaraError]) do
14
+ raise Capybara::CapybaraError if @session.windows.size != 3
15
+ end
16
+ end
17
+
18
+ after do
19
+ (@session.windows - [@window]).each(&:close)
20
+ @session.switch_to_window(@window)
21
+ end
22
+
23
+ it 'should return objects of Capybara::Window class' do
24
+ expect(@session.windows.map { |window| window.instance_of?(Capybara::Window) }).to eq([true] * 3)
25
+ end
26
+
27
+ it 'should be able to switch to windows' do
28
+ sleep 1 # give windows enough time to fully load
29
+ titles = @session.windows.map do |window|
30
+ @session.within_window(window) { @session.title }
31
+ end
32
+ expect(titles).to match_array(['With Windows', 'Title of the first popup', 'Title of popup two'])
33
+ end
34
+ end
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ Capybara::SpecHelper.spec '#within_window', requires: [:windows] do
4
+ before do
5
+ @window = @session.current_window
6
+ @session.visit('/with_windows')
7
+ @session.find(:css, '#openTwoWindows').click
8
+
9
+ @session.document.synchronize(3, errors: [Capybara::CapybaraError]) do
10
+ raise Capybara::CapybaraError if @session.windows.size != 3
11
+ end
12
+ end
13
+
14
+ after do
15
+ (@session.windows - [@window]).each do |w|
16
+ @session.switch_to_window w
17
+ w.close
18
+ end
19
+ @session.switch_to_window(@window)
20
+ end
21
+
22
+ context 'with an instance of Capybara::Window' do
23
+ it 'should not invoke driver#switch_to_window when given current window' do
24
+ allow(@session.driver).to receive(:switch_to_window).and_call_original
25
+ @session.within_window @window do
26
+ expect(@session.title).to eq('With Windows')
27
+ end
28
+ expect(@session.driver).not_to have_received(:switch_to_window)
29
+ end
30
+
31
+ it 'should be able to switch to another window' do
32
+ window = (@session.windows - [@window]).first
33
+ @session.within_window window do
34
+ expect(@session).to have_title(/Title of the first popup|Title of popup two/)
35
+ end
36
+ expect(@session.title).to eq('With Windows')
37
+ end
38
+
39
+ it 'returns value from the block' do
40
+ window = (@session.windows - [@window]).first
41
+ value = @session.within_window window do
42
+ 43252003274489856000
43
+ end
44
+ expect(value).to eq(43252003274489856000)
45
+ end
46
+
47
+ it 'should switch back if exception was raised inside block' do
48
+ window = (@session.windows - [@window]).first
49
+ expect do
50
+ @session.within_window(window) do
51
+ @session.within 'html' do
52
+ raise 'some error'
53
+ end
54
+ end
55
+ end.to raise_error(StandardError, 'some error')
56
+ expect(@session.current_window).to eq(@window)
57
+ expect(@session).to have_css('#doesNotOpenWindows')
58
+ expect(@session.send(:scopes)).to eq([nil])
59
+ end
60
+
61
+ it 'should leave correct scopes after execution in case of error', requires: %i[windows frames] do
62
+ window = (@session.windows - [@window]).first
63
+ expect do
64
+ @session.within_frame 'frameOne' do
65
+ @session.within_window(window) {}
66
+ end
67
+ end.to raise_error(Capybara::ScopeError)
68
+ expect(@session.current_window).to eq(@window)
69
+ expect(@session).to have_css('#doesNotOpenWindows')
70
+ expect(@session.send(:scopes)).to eq([nil])
71
+ end
72
+
73
+ it 'should raise error if closed window was passed' do
74
+ other_window = (@session.windows - [@window]).first
75
+ @session.within_window other_window do
76
+ other_window.close
77
+ end
78
+ expect do
79
+ @session.within_window(other_window) do
80
+ raise 'should not be invoked'
81
+ end
82
+ end.to raise_error(@session.driver.no_such_window_error)
83
+ expect(@session.current_window).to eq(@window)
84
+ expect(@session).to have_css('#doesNotOpenWindows')
85
+ expect(@session.send(:scopes)).to eq([nil])
86
+ end
87
+ end
88
+
89
+ context 'with lambda' do
90
+ it 'should find the div in another window' do
91
+ @session.within_window(-> { @session.title == 'Title of the first popup' }) do
92
+ expect(@session).to have_css('#divInPopupOne')
93
+ end
94
+ end
95
+
96
+ it 'should find divs in both windows' do
97
+ @session.within_window(-> { @session.title == 'Title of popup two' }) do
98
+ expect(@session).to have_css('#divInPopupTwo')
99
+ end
100
+ @session.within_window(-> { @session.title == 'Title of the first popup' }) do
101
+ expect(@session).to have_css('#divInPopupOne')
102
+ end
103
+ expect(@session.title).to eq('With Windows')
104
+ end
105
+
106
+ it 'should be able to nest within_window' do
107
+ @session.within_window(-> { @session.title == 'Title of popup two' }) do
108
+ expect(@session).to have_css('#divInPopupTwo')
109
+ @session.within_window(-> { @session.title == 'Title of the first popup' }) do
110
+ expect(@session).to have_css('#divInPopupOne')
111
+ end
112
+ expect(@session).to have_css('#divInPopupTwo')
113
+ expect(@session).not_to have_css('divInPopupOne')
114
+ end
115
+ expect(@session).not_to have_css('#divInPopupTwo')
116
+ expect(@session).not_to have_css('divInPopupOne')
117
+ expect(@session.title).to eq('With Windows')
118
+ end
119
+
120
+ it 'should work inside a normal scope' do
121
+ expect(@session).to have_css('#openWindow')
122
+ @session.within(:css, '#scope') do
123
+ @session.within_window(-> { @session.title == 'Title of the first popup' }) do
124
+ expect(@session).to have_css('#divInPopupOne')
125
+ end
126
+ expect(@session).to have_content('My scoped content')
127
+ expect(@session).not_to have_css('#openWindow')
128
+ end
129
+ end
130
+
131
+ it "should raise error if window wasn't found" do
132
+ expect do
133
+ @session.within_window(-> { @session.title == 'Invalid title' }) do
134
+ expect(@session).to have_css('#divInPopupOne')
135
+ end
136
+ end.to raise_error(Capybara::WindowError, 'Could not find a window matching block/lambda')
137
+ expect(@session.current_window).to eq(@window)
138
+ expect(@session).to have_css('#doesNotOpenWindows')
139
+ expect(@session.send(:scopes)).to eq([nil])
140
+ end
141
+
142
+ it 'returns value from the block' do
143
+ value = @session.within_window(-> { @session.title == 'Title of popup two' }) { 42 }
144
+ expect(value).to eq(42)
145
+ end
146
+
147
+ it 'should switch back if exception was raised inside block' do
148
+ expect do
149
+ @session.within_window(-> { @session.title == 'Title of popup two' }) do
150
+ raise 'some error'
151
+ end
152
+ end.to raise_error(StandardError, 'some error')
153
+ expect(@session.current_window).to eq(@window)
154
+ expect(@session.send(:scopes)).to eq([nil])
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,199 @@
1
+ # frozen_string_literal: true
2
+
3
+ Capybara::SpecHelper.spec '#within' do
4
+ before do
5
+ @session.visit('/with_scope')
6
+ end
7
+
8
+ context 'with CSS selector' do
9
+ it 'should click links in the given scope' do
10
+ @session.within(:css, '#for_bar li:first-child') do
11
+ @session.click_link('Go')
12
+ end
13
+ expect(@session).to have_content('Bar')
14
+ end
15
+
16
+ it 'should assert content in the given scope' do
17
+ @session.within(:css, '#for_foo') do
18
+ expect(@session).not_to have_content('First Name')
19
+ end
20
+ expect(@session).to have_content('First Name')
21
+ end
22
+
23
+ it 'should accept additional options' do
24
+ @session.within(:css, '#for_bar li', text: 'With Simple HTML') do
25
+ @session.click_link('Go')
26
+ end
27
+ expect(@session).to have_content('Bar')
28
+ end
29
+
30
+ it 'should reload the node if the page is changed' do
31
+ @session.within(:css, '#for_foo') do
32
+ @session.visit('/with_scope_other')
33
+ expect(@session).to have_content('Different text')
34
+ end
35
+ end
36
+
37
+ it 'should reload multiple nodes if the page is changed' do
38
+ @session.within(:css, '#for_bar') do
39
+ @session.within(:css, 'form[action="/redirect"]') do
40
+ @session.refresh
41
+ expect(@session).to have_content('First Name')
42
+ end
43
+ end
44
+ end
45
+
46
+ it 'should error if the page is changed and a matching node no longer exists' do
47
+ @session.within(:css, '#for_foo') do
48
+ @session.visit('/')
49
+ expect { @session.text }.to raise_error(StandardError)
50
+ end
51
+ end
52
+ end
53
+
54
+ context 'with XPath selector' do
55
+ it 'should click links in the given scope' do
56
+ @session.within(:xpath, "//div[@id='for_bar']//li[contains(.,'With Simple HTML')]") do
57
+ @session.click_link('Go')
58
+ end
59
+ expect(@session).to have_content('Bar')
60
+ end
61
+ end
62
+
63
+ context 'with the default selector' do
64
+ it 'should use XPath' do
65
+ @session.within("//div[@id='for_bar']//li[contains(.,'With Simple HTML')]") do
66
+ @session.click_link('Go')
67
+ end
68
+ expect(@session).to have_content('Bar')
69
+ end
70
+ end
71
+
72
+ context 'with Node rather than selector' do
73
+ it 'should click links in the given scope' do
74
+ node_of_interest = @session.find(:css, '#for_bar li', text: 'With Simple HTML')
75
+
76
+ @session.within(node_of_interest) do
77
+ @session.click_link('Go')
78
+ end
79
+ expect(@session).to have_content('Bar')
80
+ end
81
+ end
82
+
83
+ context 'with the default selector set to CSS' do
84
+ before { Capybara.default_selector = :css }
85
+
86
+ after { Capybara.default_selector = :xpath }
87
+
88
+ it 'should use CSS' do
89
+ @session.within('#for_bar li', text: 'With Simple HTML') do
90
+ @session.click_link('Go')
91
+ end
92
+ expect(@session).to have_content('Bar')
93
+ end
94
+ end
95
+
96
+ context 'with nested scopes' do
97
+ it 'should respect the inner scope' do
98
+ @session.within("//div[@id='for_bar']") do
99
+ @session.within(".//li[contains(.,'Bar')]") do
100
+ @session.click_link('Go')
101
+ end
102
+ end
103
+ expect(@session).to have_content('Another World')
104
+ end
105
+
106
+ it 'should respect the outer scope' do
107
+ @session.within("//div[@id='another_foo']") do
108
+ @session.within(".//li[contains(.,'With Simple HTML')]") do
109
+ @session.click_link('Go')
110
+ end
111
+ end
112
+ expect(@session).to have_content('Hello world')
113
+ end
114
+ end
115
+
116
+ it 'should raise an error if the scope is not found on the page' do
117
+ expect do
118
+ @session.within("//div[@id='doesnotexist']") do
119
+ end
120
+ end.to raise_error(Capybara::ElementNotFound)
121
+ end
122
+
123
+ it 'should restore the scope when an error is raised' do
124
+ expect do
125
+ @session.within("//div[@id='for_bar']") do
126
+ expect do
127
+ expect do
128
+ @session.within(".//div[@id='doesnotexist']") do
129
+ end
130
+ end.to raise_error(Capybara::ElementNotFound)
131
+ end.not_to change { @session.has_xpath?(".//div[@id='another_foo']") }.from(false)
132
+ end
133
+ end.not_to change { @session.has_xpath?(".//div[@id='another_foo']") }.from(true)
134
+ end
135
+
136
+ it 'should fill in a field and click a button' do
137
+ @session.within("//li[contains(.,'Bar')]") do
138
+ @session.click_button('Go')
139
+ end
140
+ expect(extract_results(@session)['first_name']).to eq('Peter')
141
+ @session.visit('/with_scope')
142
+ @session.within("//li[contains(.,'Bar')]") do
143
+ @session.fill_in('First Name', with: 'Dagobert')
144
+ @session.click_button('Go')
145
+ end
146
+ expect(extract_results(@session)['first_name']).to eq('Dagobert')
147
+ end
148
+
149
+ it 'should have #within_element as an alias' do
150
+ expect(Capybara::Session.instance_method(:within)).to eq Capybara::Session.instance_method(:within_element)
151
+ @session.within_element(:css, '#for_foo') do
152
+ expect(@session).not_to have_content('First Name')
153
+ end
154
+ end
155
+ end
156
+
157
+ Capybara::SpecHelper.spec '#within_fieldset' do
158
+ before do
159
+ @session.visit('/fieldsets')
160
+ end
161
+
162
+ it 'should restrict scope to a fieldset given by id' do
163
+ @session.within_fieldset('villain_fieldset') do
164
+ @session.fill_in('Name', with: 'Goldfinger')
165
+ @session.click_button('Create')
166
+ end
167
+ expect(extract_results(@session)['villain_name']).to eq('Goldfinger')
168
+ end
169
+
170
+ it 'should restrict scope to a fieldset given by legend' do
171
+ @session.within_fieldset('Villain') do
172
+ @session.fill_in('Name', with: 'Goldfinger')
173
+ @session.click_button('Create')
174
+ end
175
+ expect(extract_results(@session)['villain_name']).to eq('Goldfinger')
176
+ end
177
+ end
178
+
179
+ Capybara::SpecHelper.spec '#within_table' do
180
+ before do
181
+ @session.visit('/tables')
182
+ end
183
+
184
+ it 'should restrict scope to a fieldset given by id' do
185
+ @session.within_table('girl_table') do
186
+ @session.fill_in('Name', with: 'Christmas')
187
+ @session.click_button('Create')
188
+ end
189
+ expect(extract_results(@session)['girl_name']).to eq('Christmas')
190
+ end
191
+
192
+ it 'should restrict scope to a fieldset given by legend' do
193
+ @session.within_table('Villain') do
194
+ @session.fill_in('Name', with: 'Quantum')
195
+ @session.click_button('Create')
196
+ end
197
+ expect(extract_results(@session)['villain_name']).to eq('Quantum')
198
+ end
199
+ end
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec'
4
+ require 'rspec/expectations'
5
+ require 'capybara'
6
+ require 'capybara/rspec' # Required here instead of in rspec_spec to avoid RSpec deprecation warning
7
+ require 'capybara/spec/test_app'
8
+ require 'nokogiri'
9
+
10
+ Capybara.save_path = File.join(Dir.pwd, 'save_path_tmp')
11
+
12
+ module Capybara
13
+ module SpecHelper
14
+ class << self
15
+ def configure(config)
16
+ config.filter_run_excluding requires: method(:filter).to_proc
17
+ config.before { Capybara::SpecHelper.reset! }
18
+ config.after { Capybara::SpecHelper.reset! }
19
+ config.shared_context_metadata_behavior = :apply_to_host_groups
20
+ end
21
+
22
+ def reset!
23
+ Capybara.app = TestApp
24
+ Capybara.app_host = nil
25
+ Capybara.default_selector = :xpath
26
+ Capybara.default_max_wait_time = 1
27
+ Capybara.ignore_hidden_elements = true
28
+ Capybara.exact = false
29
+ Capybara.raise_server_errors = true
30
+ Capybara.visible_text_only = false
31
+ Capybara.match = :smart
32
+ Capybara.enable_aria_label = false
33
+ Capybara.default_set_options = {}
34
+ Capybara.disable_animation = false
35
+ Capybara.test_id = nil
36
+ Capybara.predicates_wait = true
37
+ Capybara.default_normalize_ws = false
38
+ Capybara.allow_gumbo = true
39
+ Capybara.w3c_click_offset = false
40
+ reset_threadsafe
41
+ end
42
+
43
+ def filter(requires, metadata)
44
+ if requires && metadata[:capybara_skip]
45
+ requires.any? do |require|
46
+ metadata[:capybara_skip].include?(require)
47
+ end
48
+ else
49
+ false
50
+ end
51
+ end
52
+
53
+ def spec(name, *options, &block)
54
+ @specs ||= []
55
+ @specs << [name, options, block]
56
+ end
57
+
58
+ def run_specs(session, name, **options, &filter_block)
59
+ specs = @specs
60
+ RSpec.describe Capybara::Session, name, options do # rubocop:disable RSpec/EmptyExampleGroup
61
+ include Capybara::SpecHelper
62
+ include Capybara::RSpecMatchers
63
+
64
+ before do |example|
65
+ @session = session
66
+ instance_exec(example, &filter_block) if filter_block
67
+ end
68
+
69
+ after do
70
+ session.reset_session!
71
+ end
72
+
73
+ before :each, psc: true do
74
+ SpecHelper.reset_threadsafe(true, session)
75
+ end
76
+
77
+ after psc: true do
78
+ SpecHelper.reset_threadsafe(false, session)
79
+ end
80
+
81
+ before :each, :exact_false do
82
+ Capybara.exact = false
83
+ end
84
+
85
+ specs.each do |spec_name, spec_options, block|
86
+ describe spec_name, *spec_options do # rubocop:disable RSpec/EmptyExampleGroup
87
+ class_eval(&block)
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ def reset_threadsafe(bool = false, session = nil)
94
+ Capybara::Session.class_variable_set(:@@instance_created, false) # Work around limit on when threadsafe can be changed
95
+ Capybara.threadsafe = bool
96
+ session = session.current_session if session.respond_to?(:current_session)
97
+ session&.instance_variable_set(:@config, nil)
98
+ end
99
+ end
100
+
101
+ def silence_stream(stream)
102
+ old_stream = stream.dup
103
+ stream.reopen(RbConfig::CONFIG['host_os'].match?(/rmswin|mingw/) ? 'NUL:' : '/dev/null')
104
+ stream.sync = true
105
+ yield
106
+ ensure
107
+ stream.reopen(old_stream)
108
+ end
109
+
110
+ def quietly
111
+ silence_stream(STDOUT) do
112
+ silence_stream(STDERR) do
113
+ yield
114
+ end
115
+ end
116
+ end
117
+
118
+ def extract_results(session)
119
+ expect(session).to have_xpath("//pre[@id='results']")
120
+ # YAML.load Nokogiri::HTML(session.body).xpath("//pre[@id='results']").first.inner_html.lstrip
121
+ YAML.load Capybara::HTML(session.body).xpath("//pre[@id='results']").first.inner_html.lstrip
122
+ end
123
+
124
+ def be_an_invalid_element_error(session)
125
+ satisfy { |error| session.driver.invalid_element_errors.any? { |e| error.is_a? e } }
126
+ end
127
+
128
+ def with_os_path_separators(path)
129
+ Gem.win_platform? ? path.to_s.tr('/', '\\') : path.to_s
130
+ end
131
+ end
132
+ end
133
+
134
+ Dir[File.dirname(__FILE__) + '/session/**/*.rb'].each { |file| require_relative file }