capybara 3.8.1 → 3.33.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (242) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/History.md +465 -0
  4. data/License.txt +1 -1
  5. data/README.md +58 -57
  6. data/lib/capybara/config.rb +10 -4
  7. data/lib/capybara/cucumber.rb +1 -1
  8. data/lib/capybara/driver/base.rb +2 -2
  9. data/lib/capybara/driver/node.rb +26 -5
  10. data/lib/capybara/dsl.rb +12 -4
  11. data/lib/capybara/helpers.rb +8 -4
  12. data/lib/capybara/minitest/spec.rb +162 -85
  13. data/lib/capybara/minitest.rb +248 -148
  14. data/lib/capybara/node/actions.rb +149 -96
  15. data/lib/capybara/node/base.rb +27 -10
  16. data/lib/capybara/node/document.rb +12 -0
  17. data/lib/capybara/node/document_matchers.rb +9 -5
  18. data/lib/capybara/node/element.rb +254 -109
  19. data/lib/capybara/node/finders.rb +83 -76
  20. data/lib/capybara/node/matchers.rb +279 -141
  21. data/lib/capybara/node/simple.rb +25 -6
  22. data/lib/capybara/queries/ancestor_query.rb +5 -7
  23. data/lib/capybara/queries/base_query.rb +11 -5
  24. data/lib/capybara/queries/current_path_query.rb +3 -3
  25. data/lib/capybara/queries/match_query.rb +1 -0
  26. data/lib/capybara/queries/selector_query.rb +467 -103
  27. data/lib/capybara/queries/sibling_query.rb +5 -4
  28. data/lib/capybara/queries/style_query.rb +6 -2
  29. data/lib/capybara/queries/text_query.rb +17 -3
  30. data/lib/capybara/queries/title_query.rb +2 -2
  31. data/lib/capybara/rack_test/browser.rb +22 -15
  32. data/lib/capybara/rack_test/driver.rb +10 -1
  33. data/lib/capybara/rack_test/errors.rb +6 -0
  34. data/lib/capybara/rack_test/form.rb +33 -28
  35. data/lib/capybara/rack_test/node.rb +74 -6
  36. data/lib/capybara/registration_container.rb +44 -0
  37. data/lib/capybara/registrations/drivers.rb +36 -0
  38. data/lib/capybara/registrations/patches/puma_ssl.rb +27 -0
  39. data/lib/capybara/registrations/servers.rb +44 -0
  40. data/lib/capybara/result.rb +55 -23
  41. data/lib/capybara/rspec/features.rb +4 -4
  42. data/lib/capybara/rspec/matcher_proxies.rb +36 -15
  43. data/lib/capybara/rspec/matchers/base.rb +111 -0
  44. data/lib/capybara/rspec/matchers/become_closed.rb +33 -0
  45. data/lib/capybara/rspec/matchers/compound.rb +88 -0
  46. data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
  47. data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
  48. data/lib/capybara/rspec/matchers/have_current_path.rb +29 -0
  49. data/lib/capybara/rspec/matchers/have_selector.rb +77 -0
  50. data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
  51. data/lib/capybara/rspec/matchers/have_text.rb +33 -0
  52. data/lib/capybara/rspec/matchers/have_title.rb +29 -0
  53. data/lib/capybara/rspec/matchers/match_selector.rb +27 -0
  54. data/lib/capybara/rspec/matchers/match_style.rb +38 -0
  55. data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
  56. data/lib/capybara/rspec/matchers.rb +117 -311
  57. data/lib/capybara/selector/builders/css_builder.rb +84 -0
  58. data/lib/capybara/selector/builders/xpath_builder.rb +69 -0
  59. data/lib/capybara/selector/css.rb +17 -15
  60. data/lib/capybara/selector/definition/button.rb +52 -0
  61. data/lib/capybara/selector/definition/checkbox.rb +26 -0
  62. data/lib/capybara/selector/definition/css.rb +10 -0
  63. data/lib/capybara/selector/definition/datalist_input.rb +35 -0
  64. data/lib/capybara/selector/definition/datalist_option.rb +25 -0
  65. data/lib/capybara/selector/definition/element.rb +27 -0
  66. data/lib/capybara/selector/definition/field.rb +40 -0
  67. data/lib/capybara/selector/definition/fieldset.rb +14 -0
  68. data/lib/capybara/selector/definition/file_field.rb +13 -0
  69. data/lib/capybara/selector/definition/fillable_field.rb +33 -0
  70. data/lib/capybara/selector/definition/frame.rb +17 -0
  71. data/lib/capybara/selector/definition/id.rb +6 -0
  72. data/lib/capybara/selector/definition/label.rb +62 -0
  73. data/lib/capybara/selector/definition/link.rb +54 -0
  74. data/lib/capybara/selector/definition/link_or_button.rb +16 -0
  75. data/lib/capybara/selector/definition/option.rb +27 -0
  76. data/lib/capybara/selector/definition/radio_button.rb +27 -0
  77. data/lib/capybara/selector/definition/select.rb +81 -0
  78. data/lib/capybara/selector/definition/table.rb +109 -0
  79. data/lib/capybara/selector/definition/table_row.rb +21 -0
  80. data/lib/capybara/selector/definition/xpath.rb +5 -0
  81. data/lib/capybara/selector/definition.rb +277 -0
  82. data/lib/capybara/selector/filter.rb +1 -0
  83. data/lib/capybara/selector/filter_set.rb +26 -19
  84. data/lib/capybara/selector/filters/base.rb +24 -5
  85. data/lib/capybara/selector/filters/expression_filter.rb +3 -3
  86. data/lib/capybara/selector/filters/locator_filter.rb +29 -0
  87. data/lib/capybara/selector/filters/node_filter.rb +16 -2
  88. data/lib/capybara/selector/regexp_disassembler.rb +214 -0
  89. data/lib/capybara/selector/selector.rb +73 -367
  90. data/lib/capybara/selector/xpath_extensions.rb +17 -0
  91. data/lib/capybara/selector.rb +221 -480
  92. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -0
  93. data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -0
  94. data/lib/capybara/selenium/atoms/src/getAttribute.js +161 -0
  95. data/lib/capybara/selenium/atoms/src/isDisplayed.js +454 -0
  96. data/lib/capybara/selenium/driver.rb +203 -86
  97. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +88 -14
  98. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +124 -0
  99. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +89 -0
  100. data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +26 -0
  101. data/lib/capybara/selenium/driver_specializations/safari_driver.rb +24 -0
  102. data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
  103. data/lib/capybara/selenium/extensions/find.rb +110 -0
  104. data/lib/capybara/selenium/extensions/html5_drag.rb +191 -22
  105. data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
  106. data/lib/capybara/selenium/extensions/scroll.rb +78 -0
  107. data/lib/capybara/selenium/logger_suppressor.rb +34 -0
  108. data/lib/capybara/selenium/node.rb +298 -93
  109. data/lib/capybara/selenium/nodes/chrome_node.rb +100 -8
  110. data/lib/capybara/selenium/nodes/edge_node.rb +104 -0
  111. data/lib/capybara/selenium/nodes/firefox_node.rb +131 -0
  112. data/lib/capybara/selenium/nodes/ie_node.rb +22 -0
  113. data/lib/capybara/selenium/nodes/safari_node.rb +118 -0
  114. data/lib/capybara/selenium/patches/action_pauser.rb +26 -0
  115. data/lib/capybara/selenium/patches/atoms.rb +18 -0
  116. data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
  117. data/lib/capybara/selenium/patches/logs.rb +45 -0
  118. data/lib/capybara/selenium/patches/pause_duration_fix.rb +1 -3
  119. data/lib/capybara/selenium/patches/persistent_client.rb +20 -0
  120. data/lib/capybara/server/animation_disabler.rb +4 -3
  121. data/lib/capybara/server/checker.rb +6 -2
  122. data/lib/capybara/server/middleware.rb +23 -13
  123. data/lib/capybara/server.rb +30 -7
  124. data/lib/capybara/session/config.rb +14 -10
  125. data/lib/capybara/session/matchers.rb +11 -7
  126. data/lib/capybara/session.rb +152 -111
  127. data/lib/capybara/spec/public/offset.js +6 -0
  128. data/lib/capybara/spec/public/test.js +101 -10
  129. data/lib/capybara/spec/session/all_spec.rb +96 -6
  130. data/lib/capybara/spec/session/ancestor_spec.rb +5 -0
  131. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +29 -0
  132. data/lib/capybara/spec/session/assert_current_path_spec.rb +5 -2
  133. data/lib/capybara/spec/session/assert_selector_spec.rb +0 -10
  134. data/lib/capybara/spec/session/assert_style_spec.rb +4 -4
  135. data/lib/capybara/spec/session/assert_text_spec.rb +9 -5
  136. data/lib/capybara/spec/session/attach_file_spec.rb +63 -36
  137. data/lib/capybara/spec/session/check_spec.rb +10 -4
  138. data/lib/capybara/spec/session/choose_spec.rb +8 -2
  139. data/lib/capybara/spec/session/click_button_spec.rb +117 -61
  140. data/lib/capybara/spec/session/click_link_or_button_spec.rb +16 -0
  141. data/lib/capybara/spec/session/click_link_spec.rb +17 -6
  142. data/lib/capybara/spec/session/element/matches_selector_spec.rb +40 -39
  143. data/lib/capybara/spec/session/evaluate_script_spec.rb +13 -0
  144. data/lib/capybara/spec/session/execute_script_spec.rb +1 -0
  145. data/lib/capybara/spec/session/fill_in_spec.rb +47 -6
  146. data/lib/capybara/spec/session/find_field_spec.rb +1 -1
  147. data/lib/capybara/spec/session/find_spec.rb +74 -4
  148. data/lib/capybara/spec/session/first_spec.rb +1 -1
  149. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +13 -1
  150. data/lib/capybara/spec/session/frame/within_frame_spec.rb +12 -1
  151. data/lib/capybara/spec/session/has_all_selectors_spec.rb +1 -1
  152. data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
  153. data/lib/capybara/spec/session/has_any_selectors_spec.rb +25 -0
  154. data/lib/capybara/spec/session/has_button_spec.rb +16 -0
  155. data/lib/capybara/spec/session/has_css_spec.rb +122 -12
  156. data/lib/capybara/spec/session/has_current_path_spec.rb +6 -4
  157. data/lib/capybara/spec/session/has_field_spec.rb +55 -0
  158. data/lib/capybara/spec/session/has_select_spec.rb +34 -6
  159. data/lib/capybara/spec/session/has_selector_spec.rb +11 -4
  160. data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
  161. data/lib/capybara/spec/session/has_table_spec.rb +166 -0
  162. data/lib/capybara/spec/session/has_text_spec.rb +48 -1
  163. data/lib/capybara/spec/session/has_xpath_spec.rb +17 -0
  164. data/lib/capybara/spec/session/html_spec.rb +7 -0
  165. data/lib/capybara/spec/session/matches_style_spec.rb +35 -0
  166. data/lib/capybara/spec/session/node_spec.rb +643 -18
  167. data/lib/capybara/spec/session/node_wrapper_spec.rb +1 -1
  168. data/lib/capybara/spec/session/refresh_spec.rb +4 -0
  169. data/lib/capybara/spec/session/reset_session_spec.rb +23 -8
  170. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +2 -2
  171. data/lib/capybara/spec/session/save_screenshot_spec.rb +4 -4
  172. data/lib/capybara/spec/session/scroll_spec.rb +117 -0
  173. data/lib/capybara/spec/session/select_spec.rb +10 -10
  174. data/lib/capybara/spec/session/selectors_spec.rb +36 -5
  175. data/lib/capybara/spec/session/uncheck_spec.rb +2 -2
  176. data/lib/capybara/spec/session/unselect_spec.rb +1 -1
  177. data/lib/capybara/spec/session/window/become_closed_spec.rb +20 -17
  178. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +4 -0
  179. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +4 -0
  180. data/lib/capybara/spec/session/window/window_spec.rb +59 -58
  181. data/lib/capybara/spec/session/window/windows_spec.rb +4 -0
  182. data/lib/capybara/spec/session/within_spec.rb +23 -0
  183. data/lib/capybara/spec/spec_helper.rb +16 -6
  184. data/lib/capybara/spec/test_app.rb +28 -23
  185. data/lib/capybara/spec/views/animated.erb +49 -0
  186. data/lib/capybara/spec/views/form.erb +48 -7
  187. data/lib/capybara/spec/views/frame_child.erb +3 -2
  188. data/lib/capybara/spec/views/frame_one.erb +1 -0
  189. data/lib/capybara/spec/views/obscured.erb +47 -0
  190. data/lib/capybara/spec/views/offset.erb +32 -0
  191. data/lib/capybara/spec/views/react.erb +45 -0
  192. data/lib/capybara/spec/views/scroll.erb +20 -0
  193. data/lib/capybara/spec/views/spatial.erb +31 -0
  194. data/lib/capybara/spec/views/tables.erb +67 -0
  195. data/lib/capybara/spec/views/with_animation.erb +29 -1
  196. data/lib/capybara/spec/views/with_dragula.erb +24 -0
  197. data/lib/capybara/spec/views/with_hover.erb +1 -0
  198. data/lib/capybara/spec/views/with_hover1.erb +10 -0
  199. data/lib/capybara/spec/views/with_html.erb +32 -6
  200. data/lib/capybara/spec/views/with_js.erb +3 -1
  201. data/lib/capybara/spec/views/with_jstree.erb +26 -0
  202. data/lib/capybara/spec/views/with_scope_other.erb +6 -0
  203. data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
  204. data/lib/capybara/version.rb +1 -1
  205. data/lib/capybara/window.rb +11 -11
  206. data/lib/capybara.rb +118 -111
  207. data/spec/basic_node_spec.rb +14 -3
  208. data/spec/capybara_spec.rb +29 -29
  209. data/spec/css_builder_spec.rb +101 -0
  210. data/spec/dsl_spec.rb +46 -21
  211. data/spec/filter_set_spec.rb +5 -5
  212. data/spec/fixtures/selenium_driver_rspec_failure.rb +1 -1
  213. data/spec/fixtures/selenium_driver_rspec_success.rb +1 -1
  214. data/spec/minitest_spec.rb +18 -4
  215. data/spec/minitest_spec_spec.rb +59 -44
  216. data/spec/rack_test_spec.rb +117 -89
  217. data/spec/regexp_dissassembler_spec.rb +250 -0
  218. data/spec/result_spec.rb +51 -49
  219. data/spec/rspec/features_spec.rb +3 -0
  220. data/spec/rspec/shared_spec_matchers.rb +112 -97
  221. data/spec/rspec_spec.rb +35 -17
  222. data/spec/sauce_spec_chrome.rb +43 -0
  223. data/spec/selector_spec.rb +244 -28
  224. data/spec/selenium_spec_chrome.rb +125 -54
  225. data/spec/selenium_spec_chrome_remote.rb +26 -12
  226. data/spec/selenium_spec_edge.rb +23 -8
  227. data/spec/selenium_spec_firefox.rb +208 -0
  228. data/spec/selenium_spec_firefox_remote.rb +15 -18
  229. data/spec/selenium_spec_ie.rb +82 -13
  230. data/spec/selenium_spec_safari.rb +148 -0
  231. data/spec/server_spec.rb +118 -77
  232. data/spec/session_spec.rb +19 -3
  233. data/spec/shared_selenium_node.rb +83 -0
  234. data/spec/shared_selenium_session.rb +110 -65
  235. data/spec/spec_helper.rb +57 -9
  236. data/spec/xpath_builder_spec.rb +93 -0
  237. metadata +257 -17
  238. data/lib/capybara/rspec/compound.rb +0 -94
  239. data/lib/capybara/selenium/driver_specializations/marionette_driver.rb +0 -49
  240. data/lib/capybara/selenium/nodes/marionette_node.rb +0 -121
  241. data/lib/capybara/spec/session/has_style_spec.rb +0 -25
  242. data/spec/selenium_spec_marionette.rb +0 -172
@@ -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
@@ -4,17 +4,17 @@ require 'spec_helper'
4
4
 
5
5
  RSpec.describe Capybara::Server do
6
6
  it 'should spool up a rack server' do
7
- @app = proc { |_env| [200, {}, ['Hello Server!']] }
8
- @server = Capybara::Server.new(@app).boot
7
+ app = proc { |_env| [200, {}, ['Hello Server!']] }
8
+ server = described_class.new(app).boot
9
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
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
 
@@ -26,12 +26,12 @@ RSpec.describe Capybara::Server do
26
26
  app = proc { |_env| [200, {}, ['Hello Server!']] }
27
27
 
28
28
  Capybara.server_host = '127.0.0.1'
29
- server = Capybara::Server.new(app).boot
29
+ server = described_class.new(app).boot
30
30
  res = Net::HTTP.get(URI("http://127.0.0.1:#{server.port}"))
31
31
  expect(res).to eq('Hello Server!')
32
32
 
33
33
  Capybara.server_host = '0.0.0.0'
34
- server = Capybara::Server.new(app).boot
34
+ server = described_class.new(app).boot
35
35
  res = Net::HTTP.get(URI("http://127.0.0.1:#{server.port}"))
36
36
  expect(res).to eq('Hello Server!')
37
37
  ensure
@@ -42,84 +42,109 @@ RSpec.describe Capybara::Server do
42
42
  it 'should use specified port' do
43
43
  Capybara.server_port = 22789
44
44
 
45
- @app = proc { |_env| [200, {}, ['Hello Server!']] }
46
- @server = Capybara::Server.new(@app).boot
45
+ app = proc { |_env| [200, {}, ['Hello Server!']] }
46
+ server = described_class.new(app).boot
47
47
 
48
- @res = Net::HTTP.start(@server.host, 22789) { |http| http.get('/') }
49
- 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')
50
50
 
51
51
  Capybara.server_port = nil
52
52
  end
53
53
 
54
54
  it 'should use given port' do
55
- @app = proc { |_env| [200, {}, ['Hello Server!']] }
56
- @server = Capybara::Server.new(@app, port: 22790).boot
55
+ app = proc { |_env| [200, {}, ['Hello Server!']] }
56
+ server = described_class.new(app, port: 22790).boot
57
57
 
58
- @res = Net::HTTP.start(@server.host, 22790) { |http| http.get('/') }
59
- 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')
60
60
 
61
61
  Capybara.server_port = nil
62
62
  end
63
63
 
64
64
  it 'should find an available port' do
65
- @app1 = proc { |_env| [200, {}, ['Hello Server!']] }
66
- @app2 = proc { |_env| [200, {}, ['Hello Second Server!']] }
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 }
67
70
 
68
- @server1 = Capybara::Server.new(@app1).boot
69
- @server2 = Capybara::Server.new(@app2).boot
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
70
76
 
71
- @res1 = Net::HTTP.start(@server1.host, @server1.port) { |http| http.get('/') }
72
- expect(@res1.body).to include('Hello Server')
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
73
88
 
74
- @res2 = Net::HTTP.start(@server2.host, @server2.port) { |http| http.get('/') }
75
- expect(@res2.body).to include('Hello Second Server')
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
76
93
  end
77
94
 
78
- it 'should support SSL' do
79
- begin
80
- key = File.join(Dir.pwd, 'spec', 'fixtures', 'key.pem')
81
- cert = File.join(Dir.pwd, 'spec', 'fixtures', 'certificate.pem')
82
- Capybara.server = :puma, { Host: "ssl://#{Capybara.server_host}?key=#{key}&cert=#{cert}" }
83
- app = proc { |_env| [200, {}, ['Hello SSL Server!']] }
84
- server = Capybara::Server.new(app).boot
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
85
101
 
86
- expect do
87
- Net::HTTP.start(server.host, server.port) { |http| http.get('/__idntify__') }
88
- end.to raise_error(EOFError)
102
+ it 'should support SSL' do
103
+ key = File.join(Dir.pwd, 'spec', 'fixtures', 'key.pem')
104
+ cert = File.join(Dir.pwd, 'spec', 'fixtures', 'certificate.pem')
105
+ Capybara.server = :puma, { Host: "ssl://#{Capybara.server_host}?key=#{key}&cert=#{cert}" }
106
+ app = proc { |_env| [200, {}, ['Hello SSL Server!']] }
107
+ server = described_class.new(app).boot
89
108
 
90
- res = Net::HTTP.start(server.host, server.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) do |https|
91
- https.get('/')
92
- end
109
+ expect do
110
+ Net::HTTP.start(server.host, server.port, max_retries: 0) { |http| http.get('/__identify__') }
111
+ end.to(raise_error do |e|
112
+ expect(e.is_a?(EOFError) || e.is_a?(Net::ReadTimeout)).to be true
113
+ end)
93
114
 
94
- expect(res.body).to include('Hello SSL Server!')
95
- ensure
96
- Capybara.server = :default
115
+ res = Net::HTTP.start(server.host, server.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) do |https|
116
+ https.get('/')
97
117
  end
118
+
119
+ expect(res.body).to include('Hello SSL Server!')
120
+ uri = ::Addressable::URI.parse(server.base_url)
121
+ expect(uri.to_hash).to include(scheme: 'https', host: server.host, port: server.port)
122
+ ensure
123
+ Capybara.server = :default
98
124
  end
99
125
 
100
126
  context 'When Capybara.reuse_server is true' do
127
+ let!(:old_reuse_server) { Capybara.reuse_server }
128
+
101
129
  before do
102
- @old_reuse_server = Capybara.reuse_server
103
130
  Capybara.reuse_server = true
104
131
  end
105
132
 
106
133
  after do
107
- Capybara.reuse_server = @old_reuse_server
134
+ Capybara.reuse_server = old_reuse_server
108
135
  end
109
136
 
110
137
  it 'should use the existing server if it already running' do
111
- @app = proc { |_env| [200, {}, ['Hello Server!']] }
112
-
113
- @server1 = Capybara::Server.new(@app).boot
114
- @server2 = Capybara::Server.new(@app).boot
138
+ app = proc { |_env| [200, {}, ['Hello Server!']] }
115
139
 
116
- res = Net::HTTP.start(@server1.host, @server1.port) { |http| http.get('/') }
117
- expect(res.body).to include('Hello Server')
140
+ servers = Array.new(2) { described_class.new(app).boot }
118
141
 
119
- res = Net::HTTP.start(@server2.host, @server2.port) { |http| http.get('/') }
120
- expect(res.body).to include('Hello Server')
142
+ servers.each do |server|
143
+ res = Net::HTTP.start(server.host, server.port) { |http| http.get('/') }
144
+ expect(res.body).to include('Hello Server')
145
+ end
121
146
 
122
- expect(@server1.port).to eq(@server2.port)
147
+ expect(servers[0].port).to eq(servers[1].port)
123
148
  end
124
149
 
125
150
  it 'detects and waits for all reused server sessions pending requests' do
@@ -132,8 +157,8 @@ RSpec.describe Capybara::Server do
132
157
  [200, {}, ['Hello Server!']]
133
158
  end
134
159
 
135
- server1 = Capybara::Server.new(app).boot
136
- server2 = Capybara::Server.new(app).boot
160
+ server1 = described_class.new(app).boot
161
+ server2 = described_class.new(app).boot
137
162
 
138
163
  expect do
139
164
  start_request(server1, 1.0)
@@ -151,22 +176,20 @@ RSpec.describe Capybara::Server do
151
176
  end
152
177
 
153
178
  after do
154
- Capybara.reuse_server = @old_reuse_server
179
+ Capybara.reuse_server = @old_reuse_server # rubocop:disable RSpec/InstanceVariable
155
180
  end
156
181
 
157
182
  it 'should not reuse an already running server' do
158
- @app = proc { |_env| [200, {}, ['Hello Server!']] }
159
-
160
- @server1 = Capybara::Server.new(@app).boot
161
- @server2 = Capybara::Server.new(@app).boot
183
+ app = proc { |_env| [200, {}, ['Hello Server!']] }
162
184
 
163
- res = Net::HTTP.start(@server1.host, @server1.port) { |http| http.get('/') }
164
- expect(res.body).to include('Hello Server')
185
+ servers = Array.new(2) { described_class.new(app).boot }
165
186
 
166
- res = Net::HTTP.start(@server2.host, @server2.port) { |http| http.get('/') }
167
- expect(res.body).to include('Hello Server')
187
+ servers.each do |server|
188
+ res = Net::HTTP.start(server.host, server.port) { |http| http.get('/') }
189
+ expect(res.body).to include('Hello Server')
190
+ end
168
191
 
169
- expect(@server1.port).not_to eq(@server2.port)
192
+ expect(servers[0].port).not_to eq(servers[1].port)
170
193
  end
171
194
 
172
195
  it 'detects and waits for only one sessions pending requests' do
@@ -179,8 +202,8 @@ RSpec.describe Capybara::Server do
179
202
  [200, {}, ['Hello Server!']]
180
203
  end
181
204
 
182
- server1 = Capybara::Server.new(app).boot
183
- server2 = Capybara::Server.new(app).boot
205
+ server1 = described_class.new(app).boot
206
+ server2 = described_class.new(app).boot
184
207
 
185
208
  expect do
186
209
  start_request(server1, 1.0)
@@ -195,24 +218,42 @@ RSpec.describe Capybara::Server do
195
218
  end
196
219
 
197
220
  it 'should raise server errors when the server errors before the timeout' do
198
- begin
199
- Capybara.register_server :kaboom do
200
- sleep 0.1
201
- raise 'kaboom'
202
- end
203
- Capybara.server = :kaboom
221
+ Capybara.register_server :kaboom do
222
+ sleep 0.1
223
+ raise 'kaboom'
224
+ end
225
+ Capybara.server = :kaboom
204
226
 
205
- expect do
206
- Capybara::Server.new(proc { |e| }).boot
207
- end.to raise_error(RuntimeError, 'kaboom')
208
- ensure
209
- Capybara.server = :default
227
+ expect do
228
+ described_class.new(proc { |e| }).boot
229
+ end.to raise_error(RuntimeError, 'kaboom')
230
+ ensure
231
+ Capybara.server = :default
232
+ end
233
+
234
+ it 'should raise an error when there are pending requests' do
235
+ app = proc do |env|
236
+ request = Rack::Request.new(env)
237
+ sleep request.params['wait_time'].to_f
238
+ [200, {}, ['Hello Server!']]
210
239
  end
240
+
241
+ server = described_class.new(app).boot
242
+
243
+ expect do
244
+ start_request(server, 59.0)
245
+ server.wait_for_pending_requests
246
+ end.not_to raise_error
247
+
248
+ expect do
249
+ start_request(server, 61.0)
250
+ server.wait_for_pending_requests
251
+ end.to raise_error('Requests did not finish in 60 seconds: ["/?wait_time=61.0"]')
211
252
  end
212
253
 
213
254
  it 'is not #responsive? when Net::HTTP raises a SystemCallError' do
214
255
  app = -> { [200, {}, ['Hello, world']] }
215
- server = Capybara::Server.new(app)
256
+ server = described_class.new(app)
216
257
  allow(Net::HTTP).to receive(:start).and_raise(SystemCallError.allocate)
217
258
  expect(server.responsive?).to eq false
218
259
  end
@@ -227,7 +268,7 @@ RSpec.describe Capybara::Server do
227
268
  response = Net::HTTPSuccess.allocate
228
269
  allow(response).to receive(:body).and_return app.object_id.to_s
229
270
  allow(Net::HTTP).to receive(:start).with(anything, anything, hash_including(use_ssl: true)).and_return(response).once
230
- Capybara::Server.new(app).boot
271
+ described_class.new(app).boot
231
272
  expect(Net::HTTP).to have_received(:start).exactly(3).times
232
273
  end
233
274
  end
data/spec/session_spec.rb CHANGED
@@ -3,16 +3,16 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  RSpec.describe Capybara::Session do
6
- context '#new' do
6
+ describe '#new' do
7
7
  it 'should raise an error if passed non-existent driver' do
8
8
  expect do
9
- Capybara::Session.new(:quox, TestApp).driver
9
+ described_class.new(:quox, TestApp).driver
10
10
  end.to raise_error(Capybara::DriverNotFoundError)
11
11
  end
12
12
 
13
13
  it 'verifies a passed app is a rack app' do
14
14
  expect do
15
- Capybara::Session.new(:unknown, random: 'hash')
15
+ described_class.new(:unknown, random: 'hash')
16
16
  end.to raise_error TypeError, 'The second parameter to Session::new should be a rack app if passed.'
17
17
  end
18
18
  end
@@ -72,4 +72,20 @@ RSpec.describe Capybara::Session do
72
72
  expect(Capybara.session_name).to eq 'sess1'
73
73
  end
74
74
  end
75
+
76
+ context 'quit' do
77
+ it 'will reset the driver' do
78
+ session = described_class.new(:rack_test, TestApp)
79
+ driver = session.driver
80
+ session.quit
81
+ expect(session.driver).not_to eql driver
82
+ end
83
+
84
+ it 'resets the document' do
85
+ session = described_class.new(:rack_test, TestApp)
86
+ document = session.document
87
+ session.quit
88
+ expect(session.document.base).not_to eql document.base
89
+ end
90
+ end
75
91
  end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'selenium-webdriver'
5
+
6
+ RSpec.shared_examples 'Capybara::Node' do |session, _mode|
7
+ let(:session) { session }
8
+
9
+ describe '#content_editable?' do
10
+ it 'returns true when the element is content editable' do
11
+ session.visit('/with_js')
12
+ expect(session.find(:css, '#existing_content_editable').base.content_editable?).to be true
13
+ expect(session.find(:css, '#existing_content_editable_child').base.content_editable?).to be true
14
+ end
15
+
16
+ it 'returns false when the element is not content editable' do
17
+ session.visit('/with_js')
18
+ expect(session.find(:css, '#drag').base.content_editable?).to be false
19
+ end
20
+ end
21
+
22
+ describe '#send_keys' do
23
+ it 'should process space' do
24
+ session.visit('/form')
25
+ session.find(:css, '#address1_city').send_keys('ocean', [:shift, :space, 'side'])
26
+ expect(session.find(:css, '#address1_city').value).to eq 'ocean SIDE'
27
+ end
28
+ end
29
+
30
+ describe '#[]' do
31
+ it 'should work for spellcheck' do
32
+ session.visit('/with_html')
33
+ expect(session.find('//input[@spellcheck="TRUE"]')[:spellcheck]).to eq('true')
34
+ expect(session.find('//input[@spellcheck="FALSE"]')[:spellcheck]).to eq('false')
35
+ end
36
+ end
37
+
38
+ describe '#set' do
39
+ it 'respects maxlength when using rapid set' do
40
+ session.visit('/form')
41
+ inp = session.find(:css, '#long_length')
42
+ value = (0...50).map { |i| ((i % 26) + 65).chr }.join
43
+ inp.set(value, rapid: true)
44
+ expect(inp.value).to eq value[0...35]
45
+ end
46
+ end
47
+
48
+ describe '#visible?' do
49
+ let(:bridge) do
50
+ session.driver.browser.send(:bridge)
51
+ end
52
+
53
+ around do |example|
54
+ native_displayed = session.driver.options[:native_displayed]
55
+ example.run
56
+ session.driver.options[:native_displayed] = native_displayed
57
+ end
58
+
59
+ before do
60
+ allow(bridge).to receive(:execute_atom).and_call_original
61
+ end
62
+
63
+ it 'will use native displayed if told to' do
64
+ pending "Chromedriver < 76.0.3809.25 doesn't support native displayed in W3C mode" if chrome_lt?(76, session) && (ENV['W3C'] != 'false')
65
+
66
+ session.driver.options[:native_displayed] = true
67
+ session.visit('/form')
68
+ session.find(:css, '#address1_city', visible: true)
69
+
70
+ expect(bridge).not_to have_received(:execute_atom)
71
+ end
72
+
73
+ it "won't use native displayed if told not to" do
74
+ skip 'Non-W3C uses native' if chrome?(session) && (ENV['W3C'] == 'false')
75
+
76
+ session.driver.options[:native_displayed] = false
77
+ session.visit('/form')
78
+ session.find(:css, '#address1_city', visible: true)
79
+
80
+ expect(bridge).to have_received(:execute_atom).with(:isDisplayed, any_args)
81
+ end
82
+ end
83
+ end