capybara 2.7.0 → 3.35.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (318) hide show
  1. checksums.yaml +5 -5
  2. data/.yardopts +1 -0
  3. data/History.md +1147 -11
  4. data/License.txt +1 -1
  5. data/README.md +252 -131
  6. data/lib/capybara/config.rb +92 -0
  7. data/lib/capybara/cucumber.rb +3 -3
  8. data/lib/capybara/driver/base.rb +52 -21
  9. data/lib/capybara/driver/node.rb +48 -14
  10. data/lib/capybara/dsl.rb +16 -9
  11. data/lib/capybara/helpers.rb +72 -81
  12. data/lib/capybara/minitest/spec.rb +267 -0
  13. data/lib/capybara/minitest.rb +385 -0
  14. data/lib/capybara/node/actions.rb +337 -89
  15. data/lib/capybara/node/base.rb +50 -32
  16. data/lib/capybara/node/document.rb +19 -3
  17. data/lib/capybara/node/document_matchers.rb +22 -24
  18. data/lib/capybara/node/element.rb +388 -125
  19. data/lib/capybara/node/finders.rb +231 -121
  20. data/lib/capybara/node/matchers.rb +503 -217
  21. data/lib/capybara/node/simple.rb +64 -27
  22. data/lib/capybara/queries/ancestor_query.rb +27 -0
  23. data/lib/capybara/queries/base_query.rb +87 -11
  24. data/lib/capybara/queries/current_path_query.rb +24 -24
  25. data/lib/capybara/queries/match_query.rb +15 -10
  26. data/lib/capybara/queries/selector_query.rb +675 -81
  27. data/lib/capybara/queries/sibling_query.rb +26 -0
  28. data/lib/capybara/queries/style_query.rb +45 -0
  29. data/lib/capybara/queries/text_query.rb +88 -20
  30. data/lib/capybara/queries/title_query.rb +9 -11
  31. data/lib/capybara/rack_test/browser.rb +63 -39
  32. data/lib/capybara/rack_test/css_handlers.rb +6 -4
  33. data/lib/capybara/rack_test/driver.rb +26 -16
  34. data/lib/capybara/rack_test/errors.rb +6 -0
  35. data/lib/capybara/rack_test/form.rb +73 -58
  36. data/lib/capybara/rack_test/node.rb +187 -67
  37. data/lib/capybara/rails.rb +4 -8
  38. data/lib/capybara/registration_container.rb +44 -0
  39. data/lib/capybara/registrations/drivers.rb +42 -0
  40. data/lib/capybara/registrations/patches/puma_ssl.rb +29 -0
  41. data/lib/capybara/registrations/servers.rb +45 -0
  42. data/lib/capybara/result.rb +142 -14
  43. data/lib/capybara/rspec/features.rb +17 -42
  44. data/lib/capybara/rspec/matcher_proxies.rb +82 -0
  45. data/lib/capybara/rspec/matchers/base.rb +111 -0
  46. data/lib/capybara/rspec/matchers/become_closed.rb +33 -0
  47. data/lib/capybara/rspec/matchers/compound.rb +88 -0
  48. data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
  49. data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
  50. data/lib/capybara/rspec/matchers/have_current_path.rb +29 -0
  51. data/lib/capybara/rspec/matchers/have_selector.rb +77 -0
  52. data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
  53. data/lib/capybara/rspec/matchers/have_text.rb +33 -0
  54. data/lib/capybara/rspec/matchers/have_title.rb +29 -0
  55. data/lib/capybara/rspec/matchers/match_selector.rb +27 -0
  56. data/lib/capybara/rspec/matchers/match_style.rb +43 -0
  57. data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
  58. data/lib/capybara/rspec/matchers.rb +143 -244
  59. data/lib/capybara/rspec.rb +10 -12
  60. data/lib/capybara/selector/builders/css_builder.rb +84 -0
  61. data/lib/capybara/selector/builders/xpath_builder.rb +71 -0
  62. data/lib/capybara/selector/css.rb +102 -0
  63. data/lib/capybara/selector/definition/button.rb +63 -0
  64. data/lib/capybara/selector/definition/checkbox.rb +26 -0
  65. data/lib/capybara/selector/definition/css.rb +10 -0
  66. data/lib/capybara/selector/definition/datalist_input.rb +35 -0
  67. data/lib/capybara/selector/definition/datalist_option.rb +25 -0
  68. data/lib/capybara/selector/definition/element.rb +28 -0
  69. data/lib/capybara/selector/definition/field.rb +40 -0
  70. data/lib/capybara/selector/definition/fieldset.rb +14 -0
  71. data/lib/capybara/selector/definition/file_field.rb +13 -0
  72. data/lib/capybara/selector/definition/fillable_field.rb +33 -0
  73. data/lib/capybara/selector/definition/frame.rb +17 -0
  74. data/lib/capybara/selector/definition/id.rb +6 -0
  75. data/lib/capybara/selector/definition/label.rb +62 -0
  76. data/lib/capybara/selector/definition/link.rb +54 -0
  77. data/lib/capybara/selector/definition/link_or_button.rb +16 -0
  78. data/lib/capybara/selector/definition/option.rb +27 -0
  79. data/lib/capybara/selector/definition/radio_button.rb +27 -0
  80. data/lib/capybara/selector/definition/select.rb +81 -0
  81. data/lib/capybara/selector/definition/table.rb +109 -0
  82. data/lib/capybara/selector/definition/table_row.rb +21 -0
  83. data/lib/capybara/selector/definition/xpath.rb +5 -0
  84. data/lib/capybara/selector/definition.rb +278 -0
  85. data/lib/capybara/selector/filter.rb +3 -46
  86. data/lib/capybara/selector/filter_set.rb +124 -0
  87. data/lib/capybara/selector/filters/base.rb +77 -0
  88. data/lib/capybara/selector/filters/expression_filter.rb +22 -0
  89. data/lib/capybara/selector/filters/locator_filter.rb +29 -0
  90. data/lib/capybara/selector/filters/node_filter.rb +31 -0
  91. data/lib/capybara/selector/regexp_disassembler.rb +214 -0
  92. data/lib/capybara/selector/selector.rb +155 -0
  93. data/lib/capybara/selector/xpath_extensions.rb +17 -0
  94. data/lib/capybara/selector.rb +232 -369
  95. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -0
  96. data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -0
  97. data/lib/capybara/selenium/atoms/src/getAttribute.js +161 -0
  98. data/lib/capybara/selenium/atoms/src/isDisplayed.js +454 -0
  99. data/lib/capybara/selenium/driver.rb +380 -142
  100. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +117 -0
  101. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +124 -0
  102. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +89 -0
  103. data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +26 -0
  104. data/lib/capybara/selenium/driver_specializations/safari_driver.rb +24 -0
  105. data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
  106. data/lib/capybara/selenium/extensions/find.rb +110 -0
  107. data/lib/capybara/selenium/extensions/html5_drag.rb +228 -0
  108. data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
  109. data/lib/capybara/selenium/extensions/scroll.rb +76 -0
  110. data/lib/capybara/selenium/logger_suppressor.rb +40 -0
  111. data/lib/capybara/selenium/node.rb +528 -97
  112. data/lib/capybara/selenium/nodes/chrome_node.rb +137 -0
  113. data/lib/capybara/selenium/nodes/edge_node.rb +104 -0
  114. data/lib/capybara/selenium/nodes/firefox_node.rb +136 -0
  115. data/lib/capybara/selenium/nodes/ie_node.rb +22 -0
  116. data/lib/capybara/selenium/nodes/safari_node.rb +118 -0
  117. data/lib/capybara/selenium/patches/action_pauser.rb +26 -0
  118. data/lib/capybara/selenium/patches/atoms.rb +18 -0
  119. data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
  120. data/lib/capybara/selenium/patches/logs.rb +45 -0
  121. data/lib/capybara/selenium/patches/pause_duration_fix.rb +9 -0
  122. data/lib/capybara/selenium/patches/persistent_client.rb +20 -0
  123. data/lib/capybara/server/animation_disabler.rb +63 -0
  124. data/lib/capybara/server/checker.rb +44 -0
  125. data/lib/capybara/server/middleware.rb +71 -0
  126. data/lib/capybara/server.rb +74 -71
  127. data/lib/capybara/session/config.rb +126 -0
  128. data/lib/capybara/session/matchers.rb +44 -27
  129. data/lib/capybara/session.rb +500 -297
  130. data/lib/capybara/spec/fixtures/no_extension +1 -0
  131. data/lib/capybara/spec/public/jquery.js +5 -5
  132. data/lib/capybara/spec/public/offset.js +6 -0
  133. data/lib/capybara/spec/public/test.js +168 -14
  134. data/lib/capybara/spec/session/accept_alert_spec.rb +37 -14
  135. data/lib/capybara/spec/session/accept_confirm_spec.rb +7 -6
  136. data/lib/capybara/spec/session/accept_prompt_spec.rb +38 -10
  137. data/lib/capybara/spec/session/all_spec.rb +179 -59
  138. data/lib/capybara/spec/session/ancestor_spec.rb +88 -0
  139. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +140 -0
  140. data/lib/capybara/spec/session/assert_current_path_spec.rb +75 -0
  141. data/lib/capybara/spec/session/assert_selector_spec.rb +143 -0
  142. data/lib/capybara/spec/session/assert_style_spec.rb +26 -0
  143. data/lib/capybara/spec/session/assert_text_spec.rb +258 -0
  144. data/lib/capybara/spec/session/assert_title_spec.rb +93 -0
  145. data/lib/capybara/spec/session/attach_file_spec.rb +154 -48
  146. data/lib/capybara/spec/session/body_spec.rb +12 -13
  147. data/lib/capybara/spec/session/check_spec.rb +168 -41
  148. data/lib/capybara/spec/session/choose_spec.rb +75 -23
  149. data/lib/capybara/spec/session/click_button_spec.rb +243 -175
  150. data/lib/capybara/spec/session/click_link_or_button_spec.rb +57 -32
  151. data/lib/capybara/spec/session/click_link_spec.rb +100 -53
  152. data/lib/capybara/spec/session/current_scope_spec.rb +11 -10
  153. data/lib/capybara/spec/session/current_url_spec.rb +61 -35
  154. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +7 -7
  155. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +5 -4
  156. data/lib/capybara/spec/session/element/{assert_match_selector.rb → assert_match_selector_spec.rb} +13 -6
  157. data/lib/capybara/spec/session/element/match_css_spec.rb +21 -7
  158. data/lib/capybara/spec/session/element/match_xpath_spec.rb +9 -7
  159. data/lib/capybara/spec/session/element/matches_selector_spec.rb +91 -34
  160. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +23 -0
  161. data/lib/capybara/spec/session/evaluate_script_spec.rb +45 -3
  162. data/lib/capybara/spec/session/execute_script_spec.rb +24 -4
  163. data/lib/capybara/spec/session/fill_in_spec.rb +166 -64
  164. data/lib/capybara/spec/session/find_button_spec.rb +37 -18
  165. data/lib/capybara/spec/session/find_by_id_spec.rb +10 -9
  166. data/lib/capybara/spec/session/find_field_spec.rb +57 -34
  167. data/lib/capybara/spec/session/find_link_spec.rb +47 -10
  168. data/lib/capybara/spec/session/find_spec.rb +290 -144
  169. data/lib/capybara/spec/session/first_spec.rb +91 -48
  170. data/lib/capybara/spec/session/frame/frame_title_spec.rb +23 -0
  171. data/lib/capybara/spec/session/frame/frame_url_spec.rb +23 -0
  172. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +116 -0
  173. data/lib/capybara/spec/session/frame/within_frame_spec.rb +112 -0
  174. data/lib/capybara/spec/session/go_back_spec.rb +3 -2
  175. data/lib/capybara/spec/session/go_forward_spec.rb +3 -2
  176. data/lib/capybara/spec/session/has_all_selectors_spec.rb +69 -0
  177. data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
  178. data/lib/capybara/spec/session/has_any_selectors_spec.rb +25 -0
  179. data/lib/capybara/spec/session/has_button_spec.rb +76 -19
  180. data/lib/capybara/spec/session/has_css_spec.rb +277 -131
  181. data/lib/capybara/spec/session/has_current_path_spec.rb +98 -26
  182. data/lib/capybara/spec/session/has_field_spec.rb +177 -107
  183. data/lib/capybara/spec/session/has_link_spec.rb +13 -12
  184. data/lib/capybara/spec/session/has_none_selectors_spec.rb +78 -0
  185. data/lib/capybara/spec/session/has_select_spec.rb +191 -95
  186. data/lib/capybara/spec/session/has_selector_spec.rb +128 -64
  187. data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
  188. data/lib/capybara/spec/session/has_table_spec.rb +172 -5
  189. data/lib/capybara/spec/session/has_text_spec.rb +126 -60
  190. data/lib/capybara/spec/session/has_title_spec.rb +35 -12
  191. data/lib/capybara/spec/session/has_xpath_spec.rb +74 -53
  192. data/lib/capybara/spec/session/{headers.rb → headers_spec.rb} +3 -2
  193. data/lib/capybara/spec/session/html_spec.rb +14 -6
  194. data/lib/capybara/spec/session/matches_style_spec.rb +35 -0
  195. data/lib/capybara/spec/session/node_spec.rb +1028 -131
  196. data/lib/capybara/spec/session/node_wrapper_spec.rb +39 -0
  197. data/lib/capybara/spec/session/refresh_spec.rb +34 -0
  198. data/lib/capybara/spec/session/reset_session_spec.rb +75 -34
  199. data/lib/capybara/spec/session/{response_code.rb → response_code_spec.rb} +2 -1
  200. data/lib/capybara/spec/session/save_and_open_page_spec.rb +3 -2
  201. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +11 -15
  202. data/lib/capybara/spec/session/save_page_spec.rb +42 -55
  203. data/lib/capybara/spec/session/save_screenshot_spec.rb +16 -14
  204. data/lib/capybara/spec/session/screenshot_spec.rb +2 -2
  205. data/lib/capybara/spec/session/scroll_spec.rb +117 -0
  206. data/lib/capybara/spec/session/select_spec.rb +112 -85
  207. data/lib/capybara/spec/session/selectors_spec.rb +71 -8
  208. data/lib/capybara/spec/session/sibling_spec.rb +52 -0
  209. data/lib/capybara/spec/session/text_spec.rb +38 -23
  210. data/lib/capybara/spec/session/title_spec.rb +17 -5
  211. data/lib/capybara/spec/session/uncheck_spec.rb +71 -12
  212. data/lib/capybara/spec/session/unselect_spec.rb +44 -43
  213. data/lib/capybara/spec/session/visit_spec.rb +99 -32
  214. data/lib/capybara/spec/session/window/become_closed_spec.rb +33 -29
  215. data/lib/capybara/spec/session/window/current_window_spec.rb +5 -3
  216. data/lib/capybara/spec/session/window/open_new_window_spec.rb +5 -3
  217. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +39 -30
  218. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +17 -10
  219. data/lib/capybara/spec/session/window/window_spec.rb +121 -73
  220. data/lib/capybara/spec/session/window/windows_spec.rb +12 -10
  221. data/lib/capybara/spec/session/window/within_window_spec.rb +52 -82
  222. data/lib/capybara/spec/session/within_spec.rb +76 -43
  223. data/lib/capybara/spec/spec_helper.rb +67 -33
  224. data/lib/capybara/spec/test_app.rb +85 -36
  225. data/lib/capybara/spec/views/animated.erb +49 -0
  226. data/lib/capybara/spec/views/buttons.erb +1 -1
  227. data/lib/capybara/spec/views/fieldsets.erb +1 -1
  228. data/lib/capybara/spec/views/form.erb +227 -20
  229. data/lib/capybara/spec/views/frame_child.erb +10 -2
  230. data/lib/capybara/spec/views/frame_one.erb +2 -1
  231. data/lib/capybara/spec/views/frame_parent.erb +2 -2
  232. data/lib/capybara/spec/views/frame_two.erb +1 -1
  233. data/lib/capybara/spec/views/header_links.erb +1 -1
  234. data/lib/capybara/spec/views/host_links.erb +1 -1
  235. data/lib/capybara/spec/views/initial_alert.erb +10 -0
  236. data/lib/capybara/spec/views/obscured.erb +47 -0
  237. data/lib/capybara/spec/views/offset.erb +32 -0
  238. data/lib/capybara/spec/views/path.erb +1 -1
  239. data/lib/capybara/spec/views/popup_one.erb +1 -1
  240. data/lib/capybara/spec/views/popup_two.erb +1 -1
  241. data/lib/capybara/spec/views/postback.erb +1 -1
  242. data/lib/capybara/spec/views/react.erb +45 -0
  243. data/lib/capybara/spec/views/scroll.erb +20 -0
  244. data/lib/capybara/spec/views/spatial.erb +31 -0
  245. data/lib/capybara/spec/views/tables.erb +69 -2
  246. data/lib/capybara/spec/views/with_animation.erb +82 -0
  247. data/lib/capybara/spec/views/with_base_tag.erb +1 -1
  248. data/lib/capybara/spec/views/with_count.erb +1 -1
  249. data/lib/capybara/spec/views/with_dragula.erb +24 -0
  250. data/lib/capybara/spec/views/with_fixed_header_footer.erb +17 -0
  251. data/lib/capybara/spec/views/with_hover.erb +7 -1
  252. data/lib/capybara/spec/views/with_hover1.erb +10 -0
  253. data/lib/capybara/spec/views/with_html.erb +100 -10
  254. data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
  255. data/lib/capybara/spec/views/with_html_entities.erb +1 -1
  256. data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
  257. data/lib/capybara/spec/views/with_js.erb +49 -3
  258. data/lib/capybara/spec/views/with_jstree.erb +26 -0
  259. data/lib/capybara/spec/views/with_namespace.erb +20 -0
  260. data/lib/capybara/spec/views/with_scope.erb +1 -1
  261. data/lib/capybara/spec/views/with_scope_other.erb +6 -0
  262. data/lib/capybara/spec/views/with_simple_html.erb +1 -1
  263. data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
  264. data/lib/capybara/spec/views/with_title.erb +1 -1
  265. data/lib/capybara/spec/views/with_unload_alert.erb +3 -1
  266. data/lib/capybara/spec/views/with_windows.erb +7 -1
  267. data/lib/capybara/spec/views/within_frames.erb +6 -3
  268. data/lib/capybara/version.rb +2 -1
  269. data/lib/capybara/window.rb +39 -21
  270. data/lib/capybara.rb +208 -186
  271. data/spec/basic_node_spec.rb +52 -39
  272. data/spec/capybara_spec.rb +72 -50
  273. data/spec/css_builder_spec.rb +101 -0
  274. data/spec/css_splitter_spec.rb +38 -0
  275. data/spec/dsl_spec.rb +81 -61
  276. data/spec/filter_set_spec.rb +46 -0
  277. data/spec/fixtures/capybara.csv +1 -0
  278. data/spec/fixtures/certificate.pem +25 -0
  279. data/spec/fixtures/key.pem +27 -0
  280. data/spec/fixtures/selenium_driver_rspec_failure.rb +7 -3
  281. data/spec/fixtures/selenium_driver_rspec_success.rb +7 -3
  282. data/spec/minitest_spec.rb +164 -0
  283. data/spec/minitest_spec_spec.rb +162 -0
  284. data/spec/per_session_config_spec.rb +68 -0
  285. data/spec/rack_test_spec.rb +189 -96
  286. data/spec/regexp_dissassembler_spec.rb +250 -0
  287. data/spec/result_spec.rb +143 -13
  288. data/spec/rspec/features_spec.rb +38 -32
  289. data/spec/rspec/scenarios_spec.rb +9 -7
  290. data/spec/rspec/shared_spec_matchers.rb +959 -0
  291. data/spec/rspec/views_spec.rb +9 -3
  292. data/spec/rspec_matchers_spec.rb +62 -0
  293. data/spec/rspec_spec.rb +127 -30
  294. data/spec/sauce_spec_chrome.rb +43 -0
  295. data/spec/selector_spec.rb +458 -37
  296. data/spec/selenium_spec_chrome.rb +196 -9
  297. data/spec/selenium_spec_chrome_remote.rb +100 -0
  298. data/spec/selenium_spec_edge.rb +47 -0
  299. data/spec/selenium_spec_firefox.rb +210 -0
  300. data/spec/selenium_spec_firefox_remote.rb +80 -0
  301. data/spec/selenium_spec_ie.rb +150 -0
  302. data/spec/selenium_spec_safari.rb +148 -0
  303. data/spec/server_spec.rb +200 -101
  304. data/spec/session_spec.rb +91 -0
  305. data/spec/shared_selenium_node.rb +83 -0
  306. data/spec/shared_selenium_session.rb +558 -0
  307. data/spec/spec_helper.rb +94 -2
  308. data/spec/xpath_builder_spec.rb +93 -0
  309. metadata +420 -60
  310. data/lib/capybara/query.rb +0 -7
  311. data/lib/capybara/spec/session/assert_current_path.rb +0 -60
  312. data/lib/capybara/spec/session/assert_selector.rb +0 -148
  313. data/lib/capybara/spec/session/assert_text.rb +0 -196
  314. data/lib/capybara/spec/session/assert_title.rb +0 -70
  315. data/lib/capybara/spec/session/source_spec.rb +0 -0
  316. data/lib/capybara/spec/session/within_frame_spec.rb +0 -53
  317. data/spec/rspec/matchers_spec.rb +0 -827
  318. data/spec/selenium_spec.rb +0 -151
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'selenium-webdriver'
5
+ require 'shared_selenium_session'
6
+ require 'shared_selenium_node'
7
+ require 'rspec/shared_spec_matchers'
8
+
9
+ def selenium_host
10
+ ENV.fetch('SELENIUM_HOST', '0.0.0.0')
11
+ end
12
+
13
+ def selenium_port
14
+ ENV.fetch('SELENIUM_PORT', 4445)
15
+ end
16
+
17
+ def ensure_selenium_running!
18
+ timer = Capybara::Helpers.timer(expire_in: 20)
19
+ begin
20
+ TCPSocket.open(selenium_host, selenium_port)
21
+ rescue StandardError
22
+ if timer.expired?
23
+ raise 'Selenium is not running. ' \
24
+ "You can run a selenium server easily with: \n" \
25
+ ' $ docker-compose up -d selenium_firefox'
26
+ else
27
+ puts 'Waiting for Selenium docker instance...'
28
+ sleep 1
29
+ retry
30
+ end
31
+ end
32
+ end
33
+
34
+ Capybara.register_driver :selenium_firefox_remote do |app|
35
+ ensure_selenium_running!
36
+
37
+ url = "http://#{selenium_host}:#{selenium_port}/wd/hub"
38
+ browser_options = ::Selenium::WebDriver::Firefox::Options.new
39
+
40
+ Capybara::Selenium::Driver.new app,
41
+ browser: :remote,
42
+ desired_capabilities: :firefox,
43
+ options: browser_options,
44
+ url: url
45
+ end
46
+
47
+ FIREFOX_REMOTE_DRIVER = :selenium_firefox_remote
48
+
49
+ module TestSessions
50
+ RemoteFirefox = Capybara::Session.new(FIREFOX_REMOTE_DRIVER, TestApp)
51
+ end
52
+
53
+ skipped_tests = %i[response_headers status_code trigger download]
54
+
55
+ Capybara::SpecHelper.run_specs TestSessions::RemoteFirefox, FIREFOX_REMOTE_DRIVER.to_s, capybara_skip: skipped_tests do |example|
56
+ case example.metadata[:full_description]
57
+ when 'Capybara::Session selenium_firefox_remote node #click should allow multiple modifiers'
58
+ skip "Firefox doesn't generate an event for shift+control+click" if firefox_gte?(62, @session)
59
+ when 'Capybara::Session selenium_firefox_remote #accept_prompt should accept the prompt with a blank response when there is a default'
60
+ pending "Geckodriver doesn't set a blank response in FF < 63 - https://bugzilla.mozilla.org/show_bug.cgi?id=1486485" if firefox_lt?(63, @session)
61
+ when 'Capybara::Session selenium_firefox_remote #attach_file with multipart form should fire change once when uploading multiple files from empty'
62
+ pending "FF < 62 doesn't support setting all files at once" if firefox_lt?(62, @session)
63
+ when 'Capybara::Session selenium_firefox_remote #reset_session! removes ALL cookies'
64
+ pending "Geckodriver doesn't provide a way to remove cookies outside the current domain"
65
+ when /#accept_confirm should work with nested modals$/
66
+ # skip because this is timing based and hence flaky when set to pending
67
+ skip 'Broken in FF 63 - https://bugzilla.mozilla.org/show_bug.cgi?id=1487358' if firefox_gte?(63, @session)
68
+ end
69
+ end
70
+
71
+ RSpec.describe 'Capybara::Session with remote firefox' do
72
+ include Capybara::SpecHelper
73
+ ['Capybara::Session', 'Capybara::Node', Capybara::RSpecMatchers].each do |examples|
74
+ include_examples examples, TestSessions::RemoteFirefox, FIREFOX_REMOTE_DRIVER
75
+ end
76
+
77
+ it 'is considered to be firefox' do
78
+ expect(session.driver.browser.browser).to eq :firefox
79
+ end
80
+ end
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'selenium-webdriver'
5
+ require 'shared_selenium_session'
6
+ require 'shared_selenium_node'
7
+ require 'rspec/shared_spec_matchers'
8
+
9
+ # if ENV['CI']
10
+ # if ::Selenium::WebDriver::Service.respond_to? :driver_path=
11
+ # ::Selenium::WebDriver::IE::Service
12
+ # else
13
+ # ::Selenium::WebDriver::IE
14
+ # end.driver_path = 'C:\Tools\WebDriver\IEDriverServer.exe'
15
+ # end
16
+
17
+ def selenium_host
18
+ ENV.fetch('SELENIUM_HOST', '192.168.56.102')
19
+ end
20
+
21
+ def selenium_port
22
+ ENV.fetch('SELENIUM_PORT', 4444)
23
+ end
24
+
25
+ def server_host
26
+ ENV.fetch('SERVER_HOST', '10.24.4.135')
27
+ end
28
+
29
+ Capybara.register_driver :selenium_ie do |app|
30
+ # ::Selenium::WebDriver.logger.level = "debug"
31
+ options = ::Selenium::WebDriver::IE::Options.new
32
+ # options.require_window_focus = true
33
+ # options.add_option("log", {"level": "trace"})
34
+
35
+ if ENV['REMOTE']
36
+ Capybara.server_host = server_host
37
+
38
+ url = "http://#{selenium_host}:#{selenium_port}/wd/hub"
39
+ Capybara::Selenium::Driver.new(app,
40
+ browser: :remote,
41
+ options: options,
42
+ url: url)
43
+ else
44
+ Capybara::Selenium::Driver.new(
45
+ app,
46
+ browser: :ie,
47
+ options: options
48
+ )
49
+ end
50
+ end
51
+
52
+ module TestSessions
53
+ SeleniumIE = Capybara::Session.new(:selenium_ie, TestApp)
54
+ end
55
+
56
+ TestSessions::SeleniumIE.current_window.resize_to(800, 500)
57
+
58
+ skipped_tests = %i[response_headers status_code trigger modals hover form_attribute windows]
59
+
60
+ Capybara::SpecHelper.log_selenium_driver_version(Selenium::WebDriver::IE) if ENV['CI']
61
+
62
+ TestSessions::SeleniumIE.current_window.resize_to(1600, 1200)
63
+
64
+ Capybara::SpecHelper.run_specs TestSessions::SeleniumIE, 'selenium', capybara_skip: skipped_tests do |example|
65
+ case example.metadata[:full_description]
66
+ when /#refresh it reposts$/
67
+ skip 'IE insists on prompting without providing a way to suppress'
68
+ when /#click_link can download a file$/
69
+ skip 'Not sure how to configure IE for automatic downloading'
70
+ when /#fill_in with Date /
71
+ pending "IE 11 doesn't support date input types"
72
+ when /#click_link_or_button with :disabled option happily clicks on links which incorrectly have the disabled attribute$/
73
+ skip 'IE 11 obeys non-standard disabled attribute on anchor tag'
74
+ when /#right_click should allow modifiers$/
75
+ skip "Windows can't :meta click because :meta triggers start menu"
76
+ when /#click should allow modifiers$/
77
+ pending "Doesn't work with IE for some unknown reason$"
78
+ when /#double_click should allow modifiers$/
79
+ pending "Doesn't work with IE for some unknown reason$"
80
+ when /#click should allow multiple modifiers$/
81
+ skip "Windows can't :meta click because :meta triggers start menu"
82
+ when /#double_click should allow multiple modifiers$/
83
+ skip "Windows can't :alt double click due to being properties shortcut"
84
+ when /#has_css\? should support case insensitive :class and :id options$/
85
+ pending "IE doesn't support case insensitive CSS selectors"
86
+ when /#reset_session! removes ALL cookies$/
87
+ pending "IE driver doesn't provide a way to remove ALL cookies"
88
+ when /#click_button should send button in document order$/
89
+ pending "IE 11 doesn't support the 'form' attribute"
90
+ when /#click_button should follow permanent redirects that maintain method$/
91
+ pending "Window 7 and 8.1 don't support 308 http status code"
92
+ when /#scroll_to can scroll an element to the center of the viewport$/,
93
+ /#scroll_to can scroll an element to the center of the scrolling element$/
94
+ pending " IE doesn't support ScrollToOptions"
95
+ when /#attach_file with multipart form should fire change once for each set of files uploaded$/,
96
+ /#attach_file with multipart form should fire change once when uploading multiple files from empty$/,
97
+ /#attach_file with multipart form should not break when using HTML5 multiple file input uploading multiple files$/
98
+ pending "IE requires all files be uploaded from same directory. Selenium doesn't provide that." if ENV['REMOTE']
99
+ when %r{#attach_file with multipart form should send content type image/jpeg when uploading an image$}
100
+ pending 'IE gets text/plain type for some reason'
101
+ # when /#click should not retry clicking when wait is disabled$/
102
+ # Fixed in IEDriverServer 3.141.0.5
103
+ # pending "IE driver doesn't error when clicking on covered elements, it just clicks the wrong element"
104
+ when /#click should go to the same page if href is blank$/
105
+ pending 'IE treats blank href as a parent request (against HTML spec)'
106
+ when /#attach_file with a block/
107
+ skip 'Hangs IE testing for unknown reason'
108
+ when /drag_to.*HTML5/
109
+ pending "IE doesn't support a DataTransfer constuctor"
110
+ when /template elements should not be visible/
111
+ skip "IE doesn't support template elements"
112
+ when /Element#drop/
113
+ pending "IE doesn't support DataTransfer constructor"
114
+ end
115
+ end
116
+
117
+ RSpec.describe 'Capybara::Session with Internet Explorer', capybara_skip: skipped_tests do # rubocop:disable RSpec/MultipleDescribes
118
+ include Capybara::SpecHelper
119
+ ['Capybara::Session', 'Capybara::Node', Capybara::RSpecMatchers].each do |examples|
120
+ include_examples examples, TestSessions::SeleniumIE, :selenium_ie
121
+ end
122
+ end
123
+
124
+ RSpec.describe Capybara::Selenium::Node do
125
+ it '#right_click should allow modifiers' do
126
+ # pending "Actions API doesn't appear to work for this"
127
+ session = TestSessions::SeleniumIE
128
+ session.visit('/with_js')
129
+ el = session.find(:css, '#click-test')
130
+ el.right_click(:control)
131
+ expect(session).to have_link('Has been control right clicked')
132
+ end
133
+
134
+ it '#click should allow multiple modifiers' do
135
+ # pending "Actions API doesn't appear to work for this"
136
+ session = TestSessions::SeleniumIE
137
+ session.visit('with_js')
138
+ # IE triggers system behavior with :meta so can't use those here
139
+ session.find(:css, '#click-test').click(:ctrl, :shift, :alt)
140
+ expect(session).to have_link('Has been alt control shift clicked')
141
+ end
142
+
143
+ it '#double_click should allow modifiers' do
144
+ # pending "Actions API doesn't appear to work for this"
145
+ session = TestSessions::SeleniumIE
146
+ session.visit('/with_js')
147
+ session.find(:css, '#click-test').double_click(:shift)
148
+ expect(session).to have_link('Has been shift double clicked')
149
+ end
150
+ end
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'selenium-webdriver'
5
+ require 'shared_selenium_session'
6
+ require 'shared_selenium_node'
7
+ require 'rspec/shared_spec_matchers'
8
+
9
+ SAFARI_DRIVER = :selenium_safari
10
+
11
+ if ::Selenium::WebDriver::Service.respond_to? :driver_path=
12
+ ::Selenium::WebDriver::Safari::Service
13
+ else
14
+ ::Selenium::WebDriver::Safari
15
+ end.driver_path = '/Applications/Safari Technology Preview.app/Contents/MacOS/safaridriver'
16
+
17
+ browser_options = ::Selenium::WebDriver::Safari::Options.new
18
+ # browser_options.headless! if ENV['HEADLESS']
19
+ # browser_options.add_option(:w3c, !!ENV['W3C'])
20
+
21
+ Capybara.register_driver :selenium_safari do |app|
22
+ Capybara::Selenium::Driver.new(app, browser: :safari, options: browser_options, timeout: 30).tap do |driver|
23
+ # driver.browser.download_path = Capybara.save_path
24
+ end
25
+ end
26
+
27
+ Capybara.register_driver :selenium_safari_not_clear_storage do |app|
28
+ safari_options = {
29
+ browser: :safari,
30
+ options: browser_options
31
+ }
32
+ Capybara::Selenium::Driver.new(app, safari_options.merge(clear_local_storage: false, clear_session_storage: false))
33
+ end
34
+
35
+ module TestSessions
36
+ Safari = Capybara::Session.new(SAFARI_DRIVER, TestApp)
37
+ end
38
+
39
+ skipped_tests = %i[response_headers status_code trigger windows drag]
40
+
41
+ Capybara::SpecHelper.log_selenium_driver_version(Selenium::WebDriver::Safari) if ENV['CI']
42
+
43
+ Capybara::SpecHelper.run_specs TestSessions::Safari, SAFARI_DRIVER.to_s, capybara_skip: skipped_tests do |example|
44
+ case example.metadata[:full_description]
45
+ when /click_link can download a file/
46
+ skip "safaridriver doesn't provide a way to set the download directory"
47
+ when /Capybara::Session selenium_safari Capybara::Window#maximize/
48
+ pending "Safari headless doesn't support maximize" if ENV['HEADLESS']
49
+ when /Capybara::Session selenium_safari #visit without a server/,
50
+ /Capybara::Session selenium_safari #visit with Capybara.app_host set should override server/,
51
+ /Capybara::Session selenium_safari #reset_session! When reuse_server == false raises any standard errors caught inside the server during a second session/
52
+ skip "Safari webdriver doesn't support multiple sessions"
53
+ when /Capybara::Session selenium_safari #click_link with alternative text given to a contained image/,
54
+ 'Capybara::Session selenium_safari #click_link_or_button with enable_aria_label should click on link'
55
+ pending 'safaridriver thinks these links are non-interactable for some unknown reason'
56
+ when /Capybara::Session selenium_safari #attach_file with a block can upload by clicking the file input/
57
+ skip "safaridriver doesn't allow clicking on file inputs"
58
+ when /Capybara::Session selenium_safari #within_frame works if the frame is closed/,
59
+ /Capybara::Session selenium_safari #switch_to_frame works if the frame is closed/
60
+ skip 'Safari has a race condition when clicking an element that causes the frame to close. It will sometimes raise a NoSuchFrameError'
61
+ when /Capybara::Session selenium_safari #reset_session! removes ALL cookies/
62
+ skip 'Safari webdriver can only remove cookies for the current domain'
63
+ when /Capybara::Session selenium_safari #refresh it reposts/
64
+ skip "Safari opens an alert that can't be closed"
65
+ when 'Capybara::Session selenium_safari node #double_click should allow to adjust the offset',
66
+ 'Capybara::Session selenium_safari node #double_click should double click an element'
67
+ pending "safardriver doesn't generate a double click event"
68
+ when 'Capybara::Session selenium_safari node #click should allow multiple modifiers',
69
+ /Capybara::Session selenium_safari node #(click|right_click|double_click) should allow modifiers/
70
+ pending "safaridriver doesn't take key state into account when clicking"
71
+ when 'Capybara::Session selenium_safari #fill_in on a pre-populated textfield with a reformatting onchange should trigger change when clearing field'
72
+ pending "safardriver clear doesn't generate change event"
73
+ when 'Capybara::Session selenium_safari #go_back should fetch a response from the driver from the previous page',
74
+ 'Capybara::Session selenium_safari #go_forward should fetch a response from the driver from the previous page'
75
+ skip 'safaridriver loses the ability to find elements in the document after `go_back`'
76
+ when /drag_to.*HTML5/
77
+ pending "Safari doesn't support"
78
+ end
79
+ end
80
+
81
+ RSpec.describe 'Capybara::Session with safari' do
82
+ include Capybara::SpecHelper
83
+ ['Capybara::Session', 'Capybara::Node', Capybara::RSpecMatchers].each do |examples|
84
+ include_examples examples, TestSessions::Safari, SAFARI_DRIVER
85
+ end
86
+
87
+ context 'storage' do
88
+ describe '#reset!' do
89
+ it 'clears storage by default' do
90
+ session = TestSessions::Safari
91
+ session.visit('/with_js')
92
+ session.find(:css, '#set-storage').click
93
+ session.reset!
94
+ session.visit('/with_js')
95
+ expect(session.evaluate_script('Object.keys(localStorage)')).to be_empty
96
+ expect(session.evaluate_script('Object.keys(sessionStorage)')).to be_empty
97
+ end
98
+
99
+ it 'does not clear storage when false' do
100
+ skip "Safari webdriver doesn't support multiple sessions"
101
+ session = Capybara::Session.new(:selenium_safari_not_clear_storage, TestApp)
102
+ session.visit('/with_js')
103
+ session.find(:css, '#set-storage').click
104
+ session.reset!
105
+ session.visit('/with_js')
106
+ expect(session.evaluate_script('Object.keys(localStorage)')).not_to be_empty
107
+ expect(session.evaluate_script('Object.keys(sessionStorage)')).not_to be_empty
108
+ end
109
+ end
110
+ end
111
+
112
+ context 'timeout' do
113
+ it 'sets the http client read timeout' do
114
+ expect(TestSessions::Safari.driver.browser.send(:bridge).http.read_timeout).to eq 30
115
+ end
116
+ end
117
+
118
+ describe 'filling in Safari-specific date and time fields with keystrokes' do
119
+ let(:datetime) { Time.new(1983, 6, 19, 6, 30) }
120
+ let(:session) { TestSessions::Safari }
121
+
122
+ before do
123
+ session.visit('/form')
124
+ end
125
+
126
+ it 'should fill in a date input with a String' do
127
+ pending "Safari doesn't support date inputs"
128
+ session.fill_in('form_date', with: '06/19/1983')
129
+ session.click_button('awesome')
130
+ expect(Date.parse(extract_results(session)['date'])).to eq datetime.to_date
131
+ end
132
+
133
+ it 'should fill in a time input with a String' do
134
+ # Safari doesn't support time inputs - so this is just a text input
135
+ session.fill_in('form_time', with: '06:30A')
136
+ session.click_button('awesome')
137
+ results = extract_results(session)['time']
138
+ expect(Time.parse(results).strftime('%r')).to eq datetime.strftime('%r')
139
+ end
140
+
141
+ it 'should fill in a datetime input with a String' do
142
+ pending "Safari doesn't support datetime inputs"
143
+ session.fill_in('form_datetime', with: "06/19/1983\t06:30A")
144
+ session.click_button('awesome')
145
+ expect(Time.parse(extract_results(session)['datetime'])).to eq datetime
146
+ end
147
+ end
148
+ end
data/spec/server_spec.rb CHANGED
@@ -1,203 +1,302 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'spec_helper'
3
4
 
4
5
  RSpec.describe Capybara::Server do
6
+ it 'should spool up a rack server' do
7
+ app = proc { |_env| [200, {}, ['Hello Server!']] }
8
+ server = described_class.new(app).boot
5
9
 
6
- it "should spool up a rack server" do
7
- @app = proc { |env| [200, {}, ["Hello Server!"]]}
8
- @server = Capybara::Server.new(@app).boot
9
-
10
- @res = Net::HTTP.start(@server.host, @server.port) { |http| http.get('/') }
10
+ res = Net::HTTP.start(server.host, server.port) { |http| http.get('/') }
11
11
 
12
- expect(@res.body).to include('Hello Server')
12
+ expect(res.body).to include('Hello Server')
13
13
  end
14
14
 
15
- it "should do nothing when no server given" do
15
+ it 'should do nothing when no server given' do
16
16
  expect do
17
- @server = Capybara::Server.new(nil).boot
17
+ described_class.new(nil).boot
18
18
  end.not_to raise_error
19
19
  end
20
20
 
21
- it "should bind to the specified host" do
21
+ it 'should bind to the specified host' do
22
+ # TODO: travis with jruby in container mode has an issue with this test
23
+ skip 'This platform has an issue with this test' if (ENV['TRAVIS'] && (RUBY_ENGINE == 'jruby')) || Gem.win_platform?
24
+
22
25
  begin
23
- app = proc { |env| [200, {}, ['Hello Server!']] }
26
+ app = proc { |_env| [200, {}, ['Hello Server!']] }
24
27
 
25
28
  Capybara.server_host = '127.0.0.1'
26
- server = Capybara::Server.new(app).boot
29
+ server = described_class.new(app).boot
27
30
  res = Net::HTTP.get(URI("http://127.0.0.1:#{server.port}"))
28
31
  expect(res).to eq('Hello Server!')
29
32
 
30
33
  Capybara.server_host = '0.0.0.0'
31
- server = Capybara::Server.new(app).boot
34
+ server = described_class.new(app).boot
32
35
  res = Net::HTTP.get(URI("http://127.0.0.1:#{server.port}"))
33
36
  expect(res).to eq('Hello Server!')
34
37
  ensure
35
38
  Capybara.server_host = nil
36
39
  end
37
- end unless ENV['TRAVIS'] and (RUBY_ENGINE == 'jruby') #TODO travis with jruby in container mode has an issue with this test
40
+ end
38
41
 
39
- it "should use specified port" do
42
+ it 'should use specified port' do
40
43
  Capybara.server_port = 22789
41
44
 
42
- @app = proc { |env| [200, {}, ["Hello Server!"]]}
43
- @server = Capybara::Server.new(@app).boot
45
+ app = proc { |_env| [200, {}, ['Hello Server!']] }
46
+ server = described_class.new(app).boot
44
47
 
45
- @res = Net::HTTP.start(@server.host, 22789) { |http| http.get('/') }
46
- expect(@res.body).to include('Hello Server')
48
+ res = Net::HTTP.start(server.host, 22789) { |http| http.get('/') }
49
+ expect(res.body).to include('Hello Server')
47
50
 
48
51
  Capybara.server_port = nil
49
52
  end
50
53
 
51
- it "should use given port" do
52
- @app = proc { |env| [200, {}, ["Hello Server!"]]}
53
- @server = Capybara::Server.new(@app, 22790).boot
54
+ it 'should use given port' do
55
+ app = proc { |_env| [200, {}, ['Hello Server!']] }
56
+ server = described_class.new(app, port: 22790).boot
54
57
 
55
- @res = Net::HTTP.start(@server.host, 22790) { |http| http.get('/') }
56
- expect(@res.body).to include('Hello Server')
58
+ res = Net::HTTP.start(server.host, 22790) { |http| http.get('/') }
59
+ expect(res.body).to include('Hello Server')
57
60
 
58
61
  Capybara.server_port = nil
59
62
  end
60
63
 
61
- it "should find an available port" do
62
- @app1 = proc { |env| [200, {}, ["Hello Server!"]]}
63
- @app2 = proc { |env| [200, {}, ["Hello Second Server!"]]}
64
+ it 'should find an available port' do
65
+ responses = ['Hello Server!', 'Hello Second Server!']
66
+ apps = responses.map do |response|
67
+ proc { |_env| [200, {}, [response]] }
68
+ end
69
+ servers = apps.map { |app| described_class.new(app).boot }
70
+
71
+ servers.each_with_index do |server, idx|
72
+ result = Net::HTTP.start(server.host, server.port) { |http| http.get('/') }
73
+ expect(result.body).to include(responses[idx])
74
+ end
75
+ end
76
+
77
+ it 'should handle that getting available ports fails randomly' do
78
+ # Use a port to force a EADDRINUSE error to be generated
79
+ server = TCPServer.new('0.0.0.0', 0)
80
+ server_port = server.addr[1]
81
+ d_server = instance_double('TCPServer', addr: [nil, server_port, nil, nil], close: nil)
82
+ call_count = 0
83
+ allow(TCPServer).to receive(:new).and_wrap_original do |m, *args|
84
+ call_count.zero? ? d_server : m.call(*args)
85
+ ensure
86
+ call_count += 1
87
+ end
64
88
 
65
- @server1 = Capybara::Server.new(@app1).boot
66
- @server2 = Capybara::Server.new(@app2).boot
89
+ port = described_class.new(Object.new, host: '0.0.0.0').port
90
+ expect(port).not_to eq(server_port)
91
+ ensure
92
+ server&.close
93
+ end
67
94
 
68
- @res1 = Net::HTTP.start(@server1.host, @server1.port) { |http| http.get('/') }
69
- expect(@res1.body).to include('Hello Server')
95
+ it 'should return its #base_url' do
96
+ app = proc { |_env| [200, {}, ['Hello Server!']] }
97
+ server = described_class.new(app).boot
98
+ uri = ::Addressable::URI.parse(server.base_url)
99
+ expect(uri.to_hash).to include(scheme: 'http', host: server.host, port: server.port)
100
+ end
70
101
 
71
- @res2 = Net::HTTP.start(@server2.host, @server2.port) { |http| http.get('/') }
72
- expect(@res2.body).to include('Hello Second Server')
102
+ it 'should call #clamp on the puma configuration to ensure that environment is a string' do
103
+ Capybara.server = :puma
104
+ app_proc = proc { |_env| [200, {}, ['Hello Puma!']] }
105
+ require 'puma'
106
+ allow(Puma::Server).to receive(:new).and_wrap_original do |method, app, events, options|
107
+ # If #clamp is not called on the puma config then this will be a Proc
108
+ expect(options.fetch(:environment)).to be_a(String)
109
+ method.call(app, events, options)
110
+ end
111
+ server = described_class.new(app_proc).boot
112
+ expect(Puma::Server).to have_received(:new).with(
113
+ anything,
114
+ anything,
115
+ satisfy { |opts| opts.final_options[:Port] == server.port }
116
+ )
117
+ ensure
118
+ Capybara.server = :default
73
119
  end
74
120
 
75
- context "When Capybara.reuse_server is true" do
121
+ it 'should support SSL' do
122
+ key = File.join(Dir.pwd, 'spec', 'fixtures', 'key.pem')
123
+ cert = File.join(Dir.pwd, 'spec', 'fixtures', 'certificate.pem')
124
+ Capybara.server = :puma, { Host: "ssl://#{Capybara.server_host}?key=#{key}&cert=#{cert}" }
125
+ app = proc { |_env| [200, {}, ['Hello SSL Server!']] }
126
+ server = described_class.new(app).boot
127
+
128
+ expect do
129
+ Net::HTTP.start(server.host, server.port, max_retries: 0) { |http| http.get('/__identify__') }
130
+ end.to(raise_error do |e|
131
+ expect(e.is_a?(EOFError) || e.is_a?(Net::ReadTimeout)).to be true
132
+ end)
133
+
134
+ res = Net::HTTP.start(server.host, server.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) do |https|
135
+ https.get('/')
136
+ end
137
+
138
+ expect(res.body).to include('Hello SSL Server!')
139
+ uri = ::Addressable::URI.parse(server.base_url)
140
+ expect(uri.to_hash).to include(scheme: 'https', host: server.host, port: server.port)
141
+ ensure
142
+ Capybara.server = :default
143
+ end
144
+
145
+ context 'When Capybara.reuse_server is true' do
146
+ let!(:old_reuse_server) { Capybara.reuse_server }
147
+
76
148
  before do
77
- @old_reuse_server = Capybara.reuse_server
78
149
  Capybara.reuse_server = true
79
150
  end
80
151
 
81
152
  after do
82
- Capybara.reuse_server = @old_reuse_server
153
+ Capybara.reuse_server = old_reuse_server
83
154
  end
84
155
 
85
- it "should use the existing server if it already running" do
86
- @app = proc { |env| [200, {}, ["Hello Server!"]]}
87
-
88
- @server1 = Capybara::Server.new(@app).boot
89
- @server2 = Capybara::Server.new(@app).boot
156
+ it 'should use the existing server if it already running' do
157
+ app = proc { |_env| [200, {}, ['Hello Server!']] }
90
158
 
91
- res = Net::HTTP.start(@server1.host, @server1.port) { |http| http.get('/') }
92
- expect(res.body).to include('Hello Server')
159
+ servers = Array.new(2) { described_class.new(app).boot }
93
160
 
94
- res = Net::HTTP.start(@server2.host, @server2.port) { |http| http.get('/') }
95
- expect(res.body).to include('Hello Server')
161
+ servers.each do |server|
162
+ res = Net::HTTP.start(server.host, server.port) { |http| http.get('/') }
163
+ expect(res.body).to include('Hello Server')
164
+ end
96
165
 
97
- expect(@server1.port).to eq(@server2.port)
166
+ expect(servers[0].port).to eq(servers[1].port)
98
167
  end
99
168
 
100
- it "detects and waits for all reused server sessions pending requests" do
101
- done = false
169
+ it 'detects and waits for all reused server sessions pending requests' do
170
+ done = 0
102
171
 
103
172
  app = proc do |env|
104
173
  request = Rack::Request.new(env)
105
174
  sleep request.params['wait_time'].to_f
106
- done = true
107
- [200, {}, ["Hello Server!"]]
175
+ done += 1
176
+ [200, {}, ['Hello Server!']]
108
177
  end
109
178
 
110
- server1 = Capybara::Server.new(app).boot
111
- server2 = Capybara::Server.new(app).boot
112
-
113
- start_request(server1, 0.5)
114
- start_request(server2, 1.0)
179
+ server1 = described_class.new(app).boot
180
+ server2 = described_class.new(app).boot
115
181
 
116
- expect {
182
+ expect do
183
+ start_request(server1, 1.0)
184
+ start_request(server2, 3.0)
117
185
  server1.wait_for_pending_requests
118
- }.to change{done}.from(false).to(true)
119
- expect(server2.instance_variable_get('@middleware').pending_requests?).to eq(false)
186
+ end.to change { done }.from(0).to(2)
187
+ expect(server2.send(:pending_requests?)).to eq(false)
120
188
  end
121
-
122
189
  end
123
190
 
124
- context "When Capybara.reuse_server is false" do
191
+ context 'When Capybara.reuse_server is false' do
125
192
  before do
126
193
  @old_reuse_server = Capybara.reuse_server
127
194
  Capybara.reuse_server = false
128
195
  end
129
196
 
130
197
  after do
131
- Capybara.reuse_server = @old_reuse_server
198
+ Capybara.reuse_server = @old_reuse_server # rubocop:disable RSpec/InstanceVariable
132
199
  end
133
200
 
134
- it "should not reuse an already running server" do
135
- @app = proc { |env| [200, {}, ["Hello Server!"]]}
136
-
137
- @server1 = Capybara::Server.new(@app).boot
138
- @server2 = Capybara::Server.new(@app).boot
201
+ it 'should not reuse an already running server' do
202
+ app = proc { |_env| [200, {}, ['Hello Server!']] }
139
203
 
140
- res = Net::HTTP.start(@server1.host, @server1.port) { |http| http.get('/') }
141
- expect(res.body).to include('Hello Server')
204
+ servers = Array.new(2) { described_class.new(app).boot }
142
205
 
143
- res = Net::HTTP.start(@server2.host, @server2.port) { |http| http.get('/') }
144
- expect(res.body).to include('Hello Server')
206
+ servers.each do |server|
207
+ res = Net::HTTP.start(server.host, server.port) { |http| http.get('/') }
208
+ expect(res.body).to include('Hello Server')
209
+ end
145
210
 
146
- expect(@server1.port).not_to eq(@server2.port)
211
+ expect(servers[0].port).not_to eq(servers[1].port)
147
212
  end
148
213
 
149
- it "detects and waits for only one sessions pending requests" do
150
- done = false
214
+ it 'detects and waits for only one sessions pending requests' do
215
+ done = 0
151
216
 
152
217
  app = proc do |env|
153
218
  request = Rack::Request.new(env)
154
219
  sleep request.params['wait_time'].to_f
155
- done = true
156
- [200, {}, ["Hello Server!"]]
220
+ done += 1
221
+ [200, {}, ['Hello Server!']]
157
222
  end
158
223
 
159
- server1 = Capybara::Server.new(app).boot
160
- server2 = Capybara::Server.new(app).boot
161
-
162
- start_request(server1, 0.5)
163
- start_request(server2, 1.0)
224
+ server1 = described_class.new(app).boot
225
+ server2 = described_class.new(app).boot
164
226
 
165
- expect {
227
+ expect do
228
+ start_request(server1, 1.0)
229
+ start_request(server2, 3.0)
166
230
  server1.wait_for_pending_requests
167
- }.to change{done}.from(false).to(true)
168
- expect(server2.instance_variable_get('@middleware').pending_requests?).to eq(true)
231
+ end.to change { done }.from(0).to(1)
232
+ expect(server2.send(:pending_requests?)).to eq(true)
233
+ expect do
234
+ server2.wait_for_pending_requests
235
+ end.to change { done }.from(1).to(2)
169
236
  end
170
-
171
237
  end
172
238
 
173
- it "should raise server errors when the server errors before the timeout" do
174
- begin
175
- Capybara.server do
176
- sleep 0.1
177
- raise 'kaboom'
178
- end
239
+ it 'should raise server errors when the server errors before the timeout' do
240
+ Capybara.register_server :kaboom do
241
+ sleep 0.1
242
+ raise 'kaboom'
243
+ end
244
+ Capybara.server = :kaboom
179
245
 
180
- expect do
181
- Capybara::Server.new(proc {|e|}).boot
182
- end.to raise_error(RuntimeError, 'kaboom')
183
- ensure
184
- # TODO refactor out the defaults so it's reliant on unset state instead of
185
- # a one-time call in capybara.rb
186
- Capybara.server {|app, port| Capybara.run_default_server(app, port)}
246
+ expect do
247
+ described_class.new(proc { |e| }).boot
248
+ end.to raise_error(RuntimeError, 'kaboom')
249
+ ensure
250
+ Capybara.server = :default
251
+ end
252
+
253
+ it 'should raise an error when there are pending requests' do
254
+ app = proc do |env|
255
+ request = Rack::Request.new(env)
256
+ sleep request.params['wait_time'].to_f
257
+ [200, {}, ['Hello Server!']]
187
258
  end
259
+
260
+ server = described_class.new(app).boot
261
+
262
+ expect do
263
+ start_request(server, 59.0)
264
+ server.wait_for_pending_requests
265
+ end.not_to raise_error
266
+
267
+ expect do
268
+ start_request(server, 61.0)
269
+ server.wait_for_pending_requests
270
+ end.to raise_error('Requests did not finish in 60 seconds: ["/?wait_time=61.0"]')
188
271
  end
189
272
 
190
- it "is not #responsive? when Net::HTTP raises a SystemCallError" do
191
- app = lambda { [200, {}, ['Hello, world']] }
192
- server = Capybara::Server.new(app)
193
- expect(Net::HTTP).to receive(:start).and_raise(SystemCallError.allocate)
273
+ it 'is not #responsive? when Net::HTTP raises a SystemCallError' do
274
+ app = -> { [200, {}, ['Hello, world']] }
275
+ server = described_class.new(app)
276
+ allow(Net::HTTP).to receive(:start).and_raise(SystemCallError.allocate)
194
277
  expect(server.responsive?).to eq false
195
278
  end
196
279
 
280
+ [EOFError, Net::ReadTimeout].each do |err|
281
+ it "should attempt an HTTPS connection if HTTP connection returns #{err}" do
282
+ app = -> { [200, {}, ['Hello, world']] }
283
+ ordered_errors = [Errno::ECONNREFUSED, err]
284
+ allow(Net::HTTP).to receive(:start).with(anything, anything, hash_excluding(:use_ssl)) do
285
+ raise ordered_errors.shift
286
+ end
287
+ response = Net::HTTPSuccess.allocate
288
+ allow(response).to receive(:body).and_return app.object_id.to_s
289
+ allow(Net::HTTP).to receive(:start).with(anything, anything, hash_including(use_ssl: true)).and_return(response).once
290
+ described_class.new(app).boot
291
+ expect(Net::HTTP).to have_received(:start).exactly(3).times
292
+ end
293
+ end
294
+
197
295
  def start_request(server, wait_time)
198
296
  # Start request, but don't wait for it to finish
199
297
  socket = TCPSocket.new(server.host, server.port)
200
- socket.write "GET /?wait_time=#{wait_time.to_s} HTTP/1.0\r\n\r\n"
298
+ socket.write "GET /?wait_time=#{wait_time} HTTP/1.0\r\n\r\n"
299
+ sleep 0.1
201
300
  socket.close
202
301
  sleep 0.1
203
302
  end