capybara 3.13.2 → 3.40.0

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 (260) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/History.md +587 -16
  4. data/README.md +240 -90
  5. data/lib/capybara/config.rb +24 -11
  6. data/lib/capybara/cucumber.rb +1 -1
  7. data/lib/capybara/driver/base.rb +8 -0
  8. data/lib/capybara/driver/node.rb +20 -4
  9. data/lib/capybara/dsl.rb +5 -3
  10. data/lib/capybara/helpers.rb +25 -4
  11. data/lib/capybara/minitest/spec.rb +174 -90
  12. data/lib/capybara/minitest.rb +256 -142
  13. data/lib/capybara/node/actions.rb +123 -77
  14. data/lib/capybara/node/base.rb +20 -12
  15. data/lib/capybara/node/document.rb +2 -2
  16. data/lib/capybara/node/document_matchers.rb +3 -3
  17. data/lib/capybara/node/element.rb +223 -117
  18. data/lib/capybara/node/finders.rb +81 -71
  19. data/lib/capybara/node/matchers.rb +271 -134
  20. data/lib/capybara/node/simple.rb +18 -5
  21. data/lib/capybara/node/whitespace_normalizer.rb +81 -0
  22. data/lib/capybara/queries/active_element_query.rb +18 -0
  23. data/lib/capybara/queries/ancestor_query.rb +8 -9
  24. data/lib/capybara/queries/base_query.rb +3 -2
  25. data/lib/capybara/queries/current_path_query.rb +15 -5
  26. data/lib/capybara/queries/selector_query.rb +364 -54
  27. data/lib/capybara/queries/sibling_query.rb +8 -6
  28. data/lib/capybara/queries/style_query.rb +2 -2
  29. data/lib/capybara/queries/text_query.rb +13 -1
  30. data/lib/capybara/queries/title_query.rb +1 -1
  31. data/lib/capybara/rack_test/browser.rb +76 -11
  32. data/lib/capybara/rack_test/driver.rb +10 -5
  33. data/lib/capybara/rack_test/errors.rb +6 -0
  34. data/lib/capybara/rack_test/form.rb +31 -9
  35. data/lib/capybara/rack_test/node.rb +74 -23
  36. data/lib/capybara/registration_container.rb +41 -0
  37. data/lib/capybara/registrations/drivers.rb +42 -0
  38. data/lib/capybara/registrations/patches/puma_ssl.rb +29 -0
  39. data/lib/capybara/registrations/servers.rb +66 -0
  40. data/lib/capybara/result.rb +44 -20
  41. data/lib/capybara/rspec/matcher_proxies.rb +13 -11
  42. data/lib/capybara/rspec/matchers/base.rb +31 -16
  43. data/lib/capybara/rspec/matchers/compound.rb +1 -1
  44. data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
  45. data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
  46. data/lib/capybara/rspec/matchers/have_current_path.rb +2 -2
  47. data/lib/capybara/rspec/matchers/have_selector.rb +21 -21
  48. data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
  49. data/lib/capybara/rspec/matchers/have_text.rb +4 -4
  50. data/lib/capybara/rspec/matchers/have_title.rb +2 -2
  51. data/lib/capybara/rspec/matchers/match_selector.rb +3 -3
  52. data/lib/capybara/rspec/matchers/match_style.rb +7 -2
  53. data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
  54. data/lib/capybara/rspec/matchers.rb +111 -68
  55. data/lib/capybara/rspec.rb +2 -0
  56. data/lib/capybara/selector/builders/css_builder.rb +11 -7
  57. data/lib/capybara/selector/builders/xpath_builder.rb +5 -3
  58. data/lib/capybara/selector/css.rb +11 -9
  59. data/lib/capybara/selector/definition/button.rb +68 -0
  60. data/lib/capybara/selector/definition/checkbox.rb +26 -0
  61. data/lib/capybara/selector/definition/css.rb +10 -0
  62. data/lib/capybara/selector/definition/datalist_input.rb +35 -0
  63. data/lib/capybara/selector/definition/datalist_option.rb +25 -0
  64. data/lib/capybara/selector/definition/element.rb +28 -0
  65. data/lib/capybara/selector/definition/field.rb +40 -0
  66. data/lib/capybara/selector/definition/fieldset.rb +14 -0
  67. data/lib/capybara/selector/definition/file_field.rb +13 -0
  68. data/lib/capybara/selector/definition/fillable_field.rb +33 -0
  69. data/lib/capybara/selector/definition/frame.rb +17 -0
  70. data/lib/capybara/selector/definition/id.rb +6 -0
  71. data/lib/capybara/selector/definition/label.rb +62 -0
  72. data/lib/capybara/selector/definition/link.rb +55 -0
  73. data/lib/capybara/selector/definition/link_or_button.rb +16 -0
  74. data/lib/capybara/selector/definition/option.rb +27 -0
  75. data/lib/capybara/selector/definition/radio_button.rb +27 -0
  76. data/lib/capybara/selector/definition/select.rb +81 -0
  77. data/lib/capybara/selector/definition/table.rb +109 -0
  78. data/lib/capybara/selector/definition/table_row.rb +21 -0
  79. data/lib/capybara/selector/definition/xpath.rb +5 -0
  80. data/lib/capybara/selector/definition.rb +280 -0
  81. data/lib/capybara/selector/filter_set.rb +19 -18
  82. data/lib/capybara/selector/filters/base.rb +11 -2
  83. data/lib/capybara/selector/filters/locator_filter.rb +13 -3
  84. data/lib/capybara/selector/regexp_disassembler.rb +11 -7
  85. data/lib/capybara/selector/selector.rb +50 -440
  86. data/lib/capybara/selector/xpath_extensions.rb +17 -0
  87. data/lib/capybara/selector.rb +473 -482
  88. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -0
  89. data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -0
  90. data/lib/capybara/selenium/atoms/src/getAttribute.js +161 -0
  91. data/lib/capybara/selenium/atoms/src/isDisplayed.js +454 -0
  92. data/lib/capybara/selenium/driver.rb +174 -62
  93. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +74 -18
  94. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +128 -0
  95. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +37 -3
  96. data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +14 -1
  97. data/lib/capybara/selenium/driver_specializations/safari_driver.rb +24 -0
  98. data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
  99. data/lib/capybara/selenium/extensions/find.rb +68 -45
  100. data/lib/capybara/selenium/extensions/html5_drag.rb +192 -22
  101. data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
  102. data/lib/capybara/selenium/extensions/scroll.rb +8 -10
  103. data/lib/capybara/selenium/node.rb +268 -72
  104. data/lib/capybara/selenium/nodes/chrome_node.rb +105 -9
  105. data/lib/capybara/selenium/nodes/edge_node.rb +110 -0
  106. data/lib/capybara/selenium/nodes/firefox_node.rb +51 -61
  107. data/lib/capybara/selenium/nodes/ie_node.rb +22 -0
  108. data/lib/capybara/selenium/nodes/safari_node.rb +118 -0
  109. data/lib/capybara/selenium/patches/atoms.rb +18 -0
  110. data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
  111. data/lib/capybara/selenium/patches/logs.rb +45 -0
  112. data/lib/capybara/selenium/patches/pause_duration_fix.rb +1 -1
  113. data/lib/capybara/selenium/patches/persistent_client.rb +20 -0
  114. data/lib/capybara/server/animation_disabler.rb +43 -21
  115. data/lib/capybara/server/checker.rb +6 -2
  116. data/lib/capybara/server/middleware.rb +25 -13
  117. data/lib/capybara/server.rb +20 -4
  118. data/lib/capybara/session/config.rb +15 -11
  119. data/lib/capybara/session/matchers.rb +11 -11
  120. data/lib/capybara/session.rb +162 -131
  121. data/lib/capybara/spec/public/offset.js +6 -0
  122. data/lib/capybara/spec/public/test.js +105 -6
  123. data/lib/capybara/spec/session/accept_alert_spec.rb +1 -1
  124. data/lib/capybara/spec/session/active_element_spec.rb +31 -0
  125. data/lib/capybara/spec/session/all_spec.rb +89 -15
  126. data/lib/capybara/spec/session/ancestor_spec.rb +5 -0
  127. data/lib/capybara/spec/session/assert_current_path_spec.rb +5 -2
  128. data/lib/capybara/spec/session/assert_text_spec.rb +26 -22
  129. data/lib/capybara/spec/session/attach_file_spec.rb +64 -31
  130. data/lib/capybara/spec/session/check_spec.rb +26 -4
  131. data/lib/capybara/spec/session/choose_spec.rb +14 -2
  132. data/lib/capybara/spec/session/click_button_spec.rb +109 -61
  133. data/lib/capybara/spec/session/click_link_or_button_spec.rb +9 -0
  134. data/lib/capybara/spec/session/click_link_spec.rb +23 -1
  135. data/lib/capybara/spec/session/current_scope_spec.rb +1 -1
  136. data/lib/capybara/spec/session/current_url_spec.rb +11 -1
  137. data/lib/capybara/spec/session/element/matches_selector_spec.rb +40 -39
  138. data/lib/capybara/spec/session/evaluate_script_spec.rb +12 -0
  139. data/lib/capybara/spec/session/fill_in_spec.rb +46 -5
  140. data/lib/capybara/spec/session/find_link_spec.rb +10 -0
  141. data/lib/capybara/spec/session/find_spec.rb +80 -7
  142. data/lib/capybara/spec/session/first_spec.rb +2 -2
  143. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +14 -1
  144. data/lib/capybara/spec/session/frame/within_frame_spec.rb +14 -1
  145. data/lib/capybara/spec/session/has_all_selectors_spec.rb +5 -5
  146. data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
  147. data/lib/capybara/spec/session/has_any_selectors_spec.rb +6 -2
  148. data/lib/capybara/spec/session/has_button_spec.rb +81 -0
  149. data/lib/capybara/spec/session/has_css_spec.rb +45 -8
  150. data/lib/capybara/spec/session/has_current_path_spec.rb +22 -7
  151. data/lib/capybara/spec/session/has_element_spec.rb +47 -0
  152. data/lib/capybara/spec/session/has_field_spec.rb +59 -1
  153. data/lib/capybara/spec/session/has_link_spec.rb +40 -0
  154. data/lib/capybara/spec/session/has_none_selectors_spec.rb +7 -7
  155. data/lib/capybara/spec/session/has_select_spec.rb +42 -8
  156. data/lib/capybara/spec/session/has_selector_spec.rb +19 -4
  157. data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
  158. data/lib/capybara/spec/session/has_table_spec.rb +177 -0
  159. data/lib/capybara/spec/session/has_text_spec.rb +31 -3
  160. data/lib/capybara/spec/session/html_spec.rb +1 -1
  161. data/lib/capybara/spec/session/matches_style_spec.rb +6 -4
  162. data/lib/capybara/spec/session/node_spec.rb +697 -23
  163. data/lib/capybara/spec/session/node_wrapper_spec.rb +1 -1
  164. data/lib/capybara/spec/session/refresh_spec.rb +2 -1
  165. data/lib/capybara/spec/session/reset_session_spec.rb +21 -7
  166. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +2 -2
  167. data/lib/capybara/spec/session/save_page_spec.rb +4 -4
  168. data/lib/capybara/spec/session/save_screenshot_spec.rb +4 -4
  169. data/lib/capybara/spec/session/scroll_spec.rb +9 -7
  170. data/lib/capybara/spec/session/select_spec.rb +5 -10
  171. data/lib/capybara/spec/session/selectors_spec.rb +24 -3
  172. data/lib/capybara/spec/session/uncheck_spec.rb +3 -3
  173. data/lib/capybara/spec/session/unselect_spec.rb +1 -1
  174. data/lib/capybara/spec/session/visit_spec.rb +20 -0
  175. data/lib/capybara/spec/session/window/become_closed_spec.rb +20 -17
  176. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +1 -1
  177. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +1 -1
  178. data/lib/capybara/spec/session/window/window_spec.rb +54 -57
  179. data/lib/capybara/spec/session/window/windows_spec.rb +2 -2
  180. data/lib/capybara/spec/session/within_spec.rb +36 -0
  181. data/lib/capybara/spec/spec_helper.rb +30 -19
  182. data/lib/capybara/spec/test_app.rb +122 -34
  183. data/lib/capybara/spec/views/animated.erb +49 -0
  184. data/lib/capybara/spec/views/form.erb +86 -8
  185. data/lib/capybara/spec/views/frame_child.erb +3 -2
  186. data/lib/capybara/spec/views/frame_one.erb +2 -1
  187. data/lib/capybara/spec/views/frame_parent.erb +1 -1
  188. data/lib/capybara/spec/views/frame_two.erb +1 -1
  189. data/lib/capybara/spec/views/initial_alert.erb +2 -1
  190. data/lib/capybara/spec/views/layout.erb +10 -0
  191. data/lib/capybara/spec/views/obscured.erb +10 -10
  192. data/lib/capybara/spec/views/offset.erb +33 -0
  193. data/lib/capybara/spec/views/path.erb +2 -2
  194. data/lib/capybara/spec/views/popup_one.erb +1 -1
  195. data/lib/capybara/spec/views/popup_two.erb +1 -1
  196. data/lib/capybara/spec/views/react.erb +45 -0
  197. data/lib/capybara/spec/views/scroll.erb +2 -1
  198. data/lib/capybara/spec/views/spatial.erb +31 -0
  199. data/lib/capybara/spec/views/tables.erb +67 -0
  200. data/lib/capybara/spec/views/with_animation.erb +39 -4
  201. data/lib/capybara/spec/views/with_base_tag.erb +2 -2
  202. data/lib/capybara/spec/views/with_dragula.erb +24 -0
  203. data/lib/capybara/spec/views/with_fixed_header_footer.erb +2 -1
  204. data/lib/capybara/spec/views/with_hover.erb +3 -2
  205. data/lib/capybara/spec/views/with_hover1.erb +10 -0
  206. data/lib/capybara/spec/views/with_html.erb +34 -6
  207. data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
  208. data/lib/capybara/spec/views/with_js.erb +7 -4
  209. data/lib/capybara/spec/views/with_jstree.erb +26 -0
  210. data/lib/capybara/spec/views/with_namespace.erb +1 -0
  211. data/lib/capybara/spec/views/with_scope.erb +2 -2
  212. data/lib/capybara/spec/views/with_scope_other.erb +6 -0
  213. data/lib/capybara/spec/views/with_shadow.erb +31 -0
  214. data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
  215. data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
  216. data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
  217. data/lib/capybara/spec/views/with_windows.erb +1 -1
  218. data/lib/capybara/spec/views/within_frames.erb +1 -1
  219. data/lib/capybara/version.rb +1 -1
  220. data/lib/capybara/window.rb +14 -18
  221. data/lib/capybara.rb +91 -126
  222. data/spec/basic_node_spec.rb +30 -16
  223. data/spec/capybara_spec.rb +40 -28
  224. data/spec/counter_spec.rb +35 -0
  225. data/spec/css_builder_spec.rb +3 -1
  226. data/spec/css_splitter_spec.rb +1 -1
  227. data/spec/dsl_spec.rb +33 -22
  228. data/spec/filter_set_spec.rb +5 -5
  229. data/spec/fixtures/selenium_driver_rspec_failure.rb +3 -3
  230. data/spec/fixtures/selenium_driver_rspec_success.rb +3 -3
  231. data/spec/minitest_spec.rb +24 -2
  232. data/spec/minitest_spec_spec.rb +60 -45
  233. data/spec/per_session_config_spec.rb +1 -1
  234. data/spec/rack_test_spec.rb +131 -98
  235. data/spec/regexp_dissassembler_spec.rb +53 -39
  236. data/spec/result_spec.rb +68 -66
  237. data/spec/rspec/features_spec.rb +9 -4
  238. data/spec/rspec/scenarios_spec.rb +6 -2
  239. data/spec/rspec/shared_spec_matchers.rb +137 -98
  240. data/spec/rspec_matchers_spec.rb +25 -0
  241. data/spec/rspec_spec.rb +23 -21
  242. data/spec/sauce_spec_chrome.rb +43 -0
  243. data/spec/selector_spec.rb +77 -21
  244. data/spec/selenium_spec_chrome.rb +141 -39
  245. data/spec/selenium_spec_chrome_remote.rb +32 -17
  246. data/spec/selenium_spec_edge.rb +36 -8
  247. data/spec/selenium_spec_firefox.rb +110 -68
  248. data/spec/selenium_spec_firefox_remote.rb +22 -15
  249. data/spec/selenium_spec_ie.rb +29 -22
  250. data/spec/selenium_spec_safari.rb +162 -0
  251. data/spec/server_spec.rb +153 -81
  252. data/spec/session_spec.rb +11 -4
  253. data/spec/shared_selenium_node.rb +79 -0
  254. data/spec/shared_selenium_session.rb +179 -74
  255. data/spec/spec_helper.rb +80 -5
  256. data/spec/whitespace_normalizer_spec.rb +54 -0
  257. data/spec/xpath_builder_spec.rb +3 -1
  258. metadata +218 -30
  259. data/lib/capybara/spec/session/source_spec.rb +0 -0
  260. data/lib/capybara/spec/views/with_title.erb +0 -5
@@ -1,93 +1,91 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Note: This file uses `sleep` to sync up parts of the tests. This is only implemented like this
3
+ # NOTE: This file uses `sleep` to sync up parts of the tests. This is only implemented like this
4
4
  # because of the methods being tested. In tests using Capybara this type of behavior should be implemented
5
5
  # using Capybara provided assertions with builtin waiting behavior.
6
6
 
7
7
  Capybara::SpecHelper.spec Capybara::Window, requires: [:windows] do
8
+ let!(:orig_window) { @session.current_window }
8
9
  before do
9
- @window = @session.current_window
10
10
  @session.visit('/with_windows')
11
11
  end
12
12
 
13
13
  after do
14
- (@session.windows - [@window]).each do |w|
14
+ (@session.windows - [orig_window]).each do |w|
15
15
  @session.switch_to_window w
16
16
  w.close
17
17
  end
18
- @session.switch_to_window(@window)
18
+ @session.switch_to_window(orig_window)
19
19
  end
20
20
 
21
21
  describe '#exists?' do
22
- before do
23
- @other_window = @session.window_opened_by do
22
+ it 'should become false after window was closed' do
23
+ other_window = @session.window_opened_by do
24
24
  @session.find(:css, '#openWindow').click
25
25
  end
26
- end
27
26
 
28
- it 'should become false after window was closed' do
29
27
  expect do
30
- @session.switch_to_window @other_window
31
- @other_window.close
32
- end.to change { @other_window.exists? }.from(true).to(false)
28
+ @session.switch_to_window other_window
29
+ other_window.close
30
+ end.to change(other_window, :exists?).from(true).to(false)
33
31
  end
34
32
  end
35
33
 
36
34
  describe '#closed?' do
37
35
  it 'should become true after window was closed' do
38
- @other_window = @session.window_opened_by do
36
+ other_window = @session.window_opened_by do
39
37
  @session.find(:css, '#openWindow').click
40
38
  end
41
39
  expect do
42
- @session.switch_to_window @other_window
43
- @other_window.close
44
- end.to change { @other_window.closed? }.from(false).to(true)
40
+ @session.switch_to_window other_window
41
+ other_window.close
42
+ end.to change { other_window.closed? }.from(false).to(true)
45
43
  end
46
44
  end
47
45
 
48
46
  describe '#current?' do
49
- before do
50
- @other_window = @session.window_opened_by do
47
+ let(:other_window) do
48
+ @session.window_opened_by do
51
49
  @session.find(:css, '#openWindow').click
52
50
  end
53
51
  end
54
52
 
55
53
  it 'should become true after switching to window' do
56
54
  expect do
57
- @session.switch_to_window(@other_window)
58
- end.to change { @other_window.current? }.from(false).to(true)
55
+ @session.switch_to_window(other_window)
56
+ end.to change(other_window, :current?).from(false).to(true)
59
57
  end
60
58
 
61
59
  it 'should return false if window is closed' do
62
- @session.switch_to_window(@other_window)
63
- @other_window.close
64
- expect(@other_window.current?).to eq(false)
60
+ @session.switch_to_window(other_window)
61
+ other_window.close
62
+ expect(other_window.current?).to be(false)
65
63
  end
66
64
  end
67
65
 
68
66
  describe '#close' do
69
- before do
70
- @other_window = @session.window_opened_by do
67
+ let!(:other_window) do
68
+ @session.window_opened_by do
71
69
  @session.find(:css, '#openWindow').click
72
70
  end
73
71
  end
74
72
 
75
73
  it 'should switch to original window if invoked not for current window' do
76
74
  expect(@session.windows.size).to eq(2)
77
- expect(@session.current_window).to eq(@window)
78
- @other_window.close
75
+ expect(@session.current_window).to eq(orig_window)
76
+ other_window.close
79
77
  expect(@session.windows.size).to eq(1)
80
- expect(@session.current_window).to eq(@window)
78
+ expect(@session.current_window).to eq(orig_window)
81
79
  end
82
80
 
83
81
  it 'should make subsequent invocations of other methods raise no_such_window_error if invoked for current window' do
84
- @session.switch_to_window(@other_window)
85
- expect(@session.current_window).to eq(@other_window)
86
- @other_window.close
82
+ @session.switch_to_window(other_window)
83
+ expect(@session.current_window).to eq(other_window)
84
+ other_window.close
87
85
  expect do
88
86
  @session.find(:css, '#some_id')
89
87
  end.to raise_error(@session.driver.no_such_window_error)
90
- @session.switch_to_window(@window)
88
+ @session.switch_to_window(orig_window)
91
89
  end
92
90
  end
93
91
 
@@ -101,55 +99,54 @@ Capybara::SpecHelper.spec Capybara::Window, requires: [:windows] do
101
99
  end
102
100
 
103
101
  it 'should switch to original window if invoked not for current window' do
104
- @other_window = @session.window_opened_by do
102
+ other_window = @session.window_opened_by do
105
103
  @session.find(:css, '#openWindow').click
106
104
  end
107
105
  sleep 1
108
- size = @session.within_window(@other_window) do
106
+ size = @session.within_window(other_window) do
109
107
  win_size
110
108
  end
111
- expect(@other_window.size).to eq(size)
112
- expect(@session.current_window).to eq(@window)
109
+ expect(other_window.size).to eq(size)
110
+ expect(@session.current_window).to eq(orig_window)
113
111
  end
114
112
  end
115
113
 
116
114
  describe '#resize_to' do
117
- before do
118
- @initial_size = @session.current_window.size
119
- end
115
+ let!(:initial_size) { @session.current_window.size }
120
116
 
121
117
  after do
122
- @session.current_window.resize_to(*@initial_size)
118
+ @session.current_window.resize_to(*initial_size)
123
119
  sleep 1
124
120
  end
125
121
 
126
122
  it 'should be able to resize window', requires: %i[windows js] do
127
- width, height = @initial_size
123
+ width, height = initial_size
128
124
  @session.current_window.resize_to(width - 100, height - 100)
129
125
  sleep 1
130
126
  expect(@session.current_window.size).to eq([width - 100, height - 100])
131
127
  end
132
128
 
133
129
  it 'should stay on current window if invoked not for current window', requires: %i[windows js] do
134
- @other_window = @session.window_opened_by do
130
+ other_window = @session.window_opened_by do
135
131
  @session.find(:css, '#openWindow').click
136
132
  end
137
- @other_window.resize_to(600, 300)
138
- expect(@session.current_window).to eq(@window)
139
133
 
140
- @session.within_window(@other_window) do
141
- expect(@session.current_window.size).to eq([600, 300])
134
+ other_window.resize_to(600, 400)
135
+ expect(@session.current_window).to eq(orig_window)
136
+
137
+ @session.within_window(other_window) do
138
+ expect(@session.current_window.size).to eq([600, 400])
142
139
  end
143
140
  end
144
141
  end
145
142
 
146
143
  describe '#maximize' do
147
- before do
148
- @initial_size = @session.current_window.size
144
+ let! :initial_size do
145
+ @session.current_window.size
149
146
  end
150
147
 
151
148
  after do
152
- @session.current_window.resize_to(*@initial_size)
149
+ @session.current_window.resize_to(*initial_size)
153
150
  sleep 0.5
154
151
  end
155
152
 
@@ -169,31 +166,31 @@ Capybara::SpecHelper.spec Capybara::Window, requires: [:windows] do
169
166
  end
170
167
 
171
168
  it 'should stay on current window if invoked not for current window', requires: %i[windows js] do
172
- @other_window = @session.window_opened_by do
169
+ other_window = @session.window_opened_by do
173
170
  @session.find(:css, '#openWindow').click
174
171
  end
175
- @other_window.resize_to(400, 300)
172
+ other_window.resize_to(400, 300)
176
173
  sleep 0.5
177
- @other_window.maximize
174
+ other_window.maximize
178
175
  sleep 0.5 # The timing on maximize is finicky on Travis -- wait a bit for maximize to occur
179
176
 
180
- expect(@session.current_window).to eq(@window)
177
+ expect(@session.current_window).to eq(orig_window)
181
178
  # Maximizing the browser affects all tabs so this may not be valid in real browsers
182
- # expect(@session.current_window.size).to eq(@initial_size)
179
+ # expect(@session.current_window.size).to eq(initial_size)
183
180
 
184
- ow_width, ow_height = @other_window.size
181
+ ow_width, ow_height = other_window.size
185
182
  expect(ow_width).to be > 400
186
183
  expect(ow_height).to be > 300
187
184
  end
188
185
  end
189
186
 
190
187
  describe '#fullscreen' do
191
- before do
192
- @initial_size = @session.current_window.size
188
+ let! :initial_size do
189
+ @session.current_window.size
193
190
  end
194
191
 
195
192
  after do
196
- @session.current_window.resize_to(*@initial_size)
193
+ @session.current_window.resize_to(*initial_size)
197
194
  sleep 1
198
195
  end
199
196
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Note: This file uses `sleep` to sync up parts of the tests. This is only implemented like this
3
+ # NOTE: This file uses `sleep` to sync up parts of the tests. This is only implemented like this
4
4
  # because of the methods being tested. In tests using Capybara this type of behavior should be implemented
5
5
  # using Capybara provided assertions with builtin waiting behavior.
6
6
 
@@ -29,6 +29,6 @@ Capybara::SpecHelper.spec '#windows', requires: [:windows] do
29
29
  titles = @session.windows.map do |window|
30
30
  @session.within_window(window) { @session.title }
31
31
  end
32
- expect(titles).to match_array(['With Windows', 'Title of the first popup', 'Title of popup two'])
32
+ expect(titles).to match_array(['With Windows', 'Title of the first popup', 'Title of popup two']) # rubocop:disable RSpec/MatchArray
33
33
  end
34
34
  end
@@ -26,6 +26,42 @@ Capybara::SpecHelper.spec '#within' do
26
26
  end
27
27
  expect(@session).to have_content('Bar')
28
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
+
53
+ it 'should pass scope element to the block' do
54
+ @session.within(:css, '#another_foo') do |scope|
55
+ expect(scope).to match_css('#another_foo')
56
+ end
57
+ end
58
+
59
+ it 'should scope click', requires: [:js] do
60
+ @session.within(:css, '#another_foo') do |scope|
61
+ @session.click
62
+ expect(scope).to have_text('I was clicked')
63
+ end
64
+ end
29
65
  end
30
66
 
31
67
  context 'with XPath selector' do
@@ -24,18 +24,21 @@ module Capybara
24
24
  Capybara.app_host = nil
25
25
  Capybara.default_selector = :xpath
26
26
  Capybara.default_max_wait_time = 1
27
+ Capybara.default_retry_interval = 0.01
27
28
  Capybara.ignore_hidden_elements = true
28
29
  Capybara.exact = false
29
30
  Capybara.raise_server_errors = true
30
31
  Capybara.visible_text_only = false
31
32
  Capybara.match = :smart
32
33
  Capybara.enable_aria_label = false
34
+ Capybara.enable_aria_role = false
33
35
  Capybara.default_set_options = {}
34
36
  Capybara.disable_animation = false
35
37
  Capybara.test_id = nil
36
38
  Capybara.predicates_wait = true
37
39
  Capybara.default_normalize_ws = false
38
- Capybara.allow_gumbo = true
40
+ Capybara.use_html5_parsing = !ENV['HTML5_PARSING'].nil?
41
+ Capybara.w3c_click_offset = true
39
42
  reset_threadsafe
40
43
  end
41
44
 
@@ -56,10 +59,10 @@ module Capybara
56
59
 
57
60
  def run_specs(session, name, **options, &filter_block)
58
61
  specs = @specs
59
- RSpec.describe Capybara::Session, name, options do # rubocop:disable RSpec/EmptyExampleGroup
62
+ RSpec.describe Capybara::Session, name, options do
60
63
  include Capybara::SpecHelper
61
64
  include Capybara::RSpecMatchers
62
- # rubocop:disable RSpec/ScatteredSetup
65
+
63
66
  before do |example|
64
67
  @session = session
65
68
  instance_exec(example, &filter_block) if filter_block
@@ -69,29 +72,29 @@ module Capybara
69
72
  session.reset_session!
70
73
  end
71
74
 
72
- before :each, psc: true do
73
- SpecHelper.reset_threadsafe(true, session)
75
+ before :each, :psc do
76
+ SpecHelper.reset_threadsafe(bool: true, session: session)
74
77
  end
75
78
 
76
79
  after psc: true do
77
- SpecHelper.reset_threadsafe(false, session)
80
+ SpecHelper.reset_threadsafe(session: session)
78
81
  end
79
82
 
80
83
  before :each, :exact_false do
81
84
  Capybara.exact = false
82
85
  end
83
- # rubocop:enable RSpec/ScatteredSetup
84
86
 
85
87
  specs.each do |spec_name, spec_options, block|
86
- describe spec_name, *spec_options do # rubocop:disable RSpec/EmptyExampleGroup
88
+ describe spec_name, *spec_options do
87
89
  class_eval(&block)
88
90
  end
89
91
  end
90
92
  end
91
93
  end
92
94
 
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
+ def reset_threadsafe(bool: false, session: nil)
96
+ # Work around limit on when threadsafe can be changed
97
+ Capybara::Session.class_variable_set(:@@instance_created, false) # rubocop:disable Style/ClassVars
95
98
  Capybara.threadsafe = bool
96
99
  session = session.current_session if session.respond_to?(:current_session)
97
100
  session&.instance_variable_set(:@config, nil)
@@ -100,31 +103,39 @@ module Capybara
100
103
 
101
104
  def silence_stream(stream)
102
105
  old_stream = stream.dup
103
- stream.reopen(RbConfig::CONFIG['host_os'] =~ /rmswin|mingw/ ? 'NUL:' : '/dev/null')
106
+ stream.reopen(RbConfig::CONFIG['host_os'].match?(/rmswin|mingw/) ? 'NUL:' : '/dev/null')
104
107
  stream.sync = true
105
108
  yield
106
109
  ensure
107
110
  stream.reopen(old_stream)
108
111
  end
109
112
 
110
- def quietly
111
- silence_stream(STDOUT) do
112
- silence_stream(STDERR) do
113
- yield
114
- end
113
+ def quietly(&block)
114
+ silence_stream($stdout) do
115
+ silence_stream($stderr, &block)
115
116
  end
116
117
  end
117
118
 
118
119
  def extract_results(session)
119
120
  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
121
+ perms = [(::Sinatra::IndifferentHash if defined? ::Sinatra::IndifferentHash)].compact
122
+ results = Capybara::HTML(session.body).xpath("//pre[@id='results']").first.inner_html.lstrip
123
+ YAML.safe_load results, permitted_classes: perms
124
+ end
125
+
126
+ def extract_content_type(session)
127
+ expect(session).to have_xpath("//pre[@id='content_type']")
128
+ Capybara::HTML(session.body).xpath("//pre[@id='content_type']").first.text
122
129
  end
123
130
 
124
131
  def be_an_invalid_element_error(session)
125
132
  satisfy { |error| session.driver.invalid_element_errors.any? { |e| error.is_a? e } }
126
133
  end
134
+
135
+ def with_os_path_separators(path)
136
+ Gem.win_platform? ? path.to_s.tr('/', '\\') : path.to_s
137
+ end
127
138
  end
128
139
  end
129
140
 
130
- Dir[File.dirname(__FILE__) + '/session/**/*.rb'].each { |file| require_relative file }
141
+ Dir["#{File.dirname(__FILE__)}/session/**/*.rb"].each { |file| require_relative file }
@@ -6,9 +6,11 @@ require 'rack'
6
6
  require 'yaml'
7
7
 
8
8
  class TestApp < Sinatra::Base
9
- class TestAppError < StandardError; end
10
- class TestAppOtherError < StandardError
9
+ class TestAppError < Exception; end # rubocop:disable Lint/InheritException
10
+
11
+ class TestAppOtherError < Exception # rubocop:disable Lint/InheritException
11
12
  def initialize(string1, msg)
13
+ super()
12
14
  @something = string1
13
15
  @message = msg
14
16
  end
@@ -18,7 +20,6 @@ class TestApp < Sinatra::Base
18
20
  set :raise_errors, true
19
21
  set :show_exceptions, false
20
22
 
21
- @@form_post_count = 0
22
23
  # Also check lib/capybara/spec/views/*.erb for pages not listed here
23
24
 
24
25
  get '/' do
@@ -34,6 +35,10 @@ class TestApp < Sinatra::Base
34
35
  redirect '/redirect_again'
35
36
  end
36
37
 
38
+ get '/redirect_with_fragment' do
39
+ redirect '/landed#with_fragment'
40
+ end
41
+
37
42
  get '/redirect_again' do
38
43
  redirect '/landed'
39
44
  end
@@ -48,8 +53,8 @@ class TestApp < Sinatra::Base
48
53
 
49
54
  get '/referer_base' do
50
55
  '<a href="/get_referer">direct link</a>' \
51
- '<a href="/redirect_to_get_referer">link via redirect</a>' \
52
- '<form action="/get_referer" method="get"><input type="submit"></form>'
56
+ '<a href="/redirect_to_get_referer">link via redirect</a>' \
57
+ '<form action="/get_referer" method="get"><input type="submit"></form>'
53
58
  end
54
59
 
55
60
  get '/redirect_to_get_referer' do
@@ -86,11 +91,11 @@ class TestApp < Sinatra::Base
86
91
  end
87
92
 
88
93
  get '/form/get' do
89
- '<pre id="results">' + params[:form].to_yaml + '</pre>'
94
+ %(<pre id="results">#{params[:form].to_yaml}</pre>)
90
95
  end
91
96
 
92
97
  post '/relative' do
93
- '<pre id="results">' + params[:form].to_yaml + '</pre>'
98
+ %(<pre id="results">#{params[:form].to_yaml}</pre>)
94
99
  end
95
100
 
96
101
  get '/favicon.ico' do
@@ -158,60 +163,143 @@ class TestApp < Sinatra::Base
158
163
 
159
164
  get '/with_title' do
160
165
  <<-HTML
161
- <title>#{params[:title] || 'Test Title'}</title>
162
- <body>
163
- <svg><title>abcdefg</title></svg>
164
- </body>
166
+ <!DOCTYPE html>
167
+ <html lang="en">
168
+ <head>
169
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
170
+ <title>#{params[:title] || 'Test Title'}</title>
171
+ </head>
172
+
173
+ <body>
174
+ <svg><title>abcdefg</title></svg>
175
+ </body>
176
+ </html>
177
+ HTML
178
+ end
179
+
180
+ get '/with_iframe' do
181
+ <<-HTML
182
+ <!DOCTYPE html>
183
+ <html lang="en">
184
+ <head>
185
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
186
+ <title>Test with Iframe</title>
187
+ </head>
188
+
189
+ <body>
190
+ <iframe src="#{params[:url]}" id="#{params[:id]}"></iframe>
191
+ </body>
192
+ </html>
193
+ HTML
194
+ end
195
+
196
+ get '/base/with_base' do
197
+ <<-HTML
198
+ <!DOCTYPE html>
199
+ <html>
200
+ <head>
201
+ <base href="/">
202
+ <title>Origin</title>
203
+ </head>
204
+ <body>
205
+ <a href="with_title">Title page</a>
206
+ <a href="?a=3">Bare query</a>
207
+ </body>
208
+ </html>
209
+ HTML
210
+ end
211
+
212
+ get '/base/with_other_base' do
213
+ <<-HTML
214
+ <!DOCTYPE html>
215
+ <html>
216
+ <head>
217
+ <base href="/base/">
218
+ <title>Origin</title>
219
+ </head>
220
+ <body>
221
+ <a href="with_title">Title page</a>
222
+ <a href="?a=3">Bare query</a>
223
+ </body>
224
+ </html>
225
+ HTML
226
+ end
227
+
228
+ get '/csp' do
229
+ response.headers['Content-Security-Policy'] = "default-src 'none'; connect-src 'self'; base-uri 'none'; font-src 'self'; img-src 'self' data:; object-src 'none'; script-src 'self' 'nonce-jAviMuMisoTisVXjgLoWdA=='; style-src 'self' 'nonce-jAviMuMisoTisVXjgLoWdA=='; form-action 'self';"
230
+ <<-HTML
231
+ <!DOCTYPE html>
232
+ <html lang="en">
233
+ <head>
234
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
235
+ <title>CSP</title>
236
+ </head>
237
+
238
+ <body>
239
+ <div>CSP</div>
240
+ </body>
241
+ </html>
165
242
  HTML
166
243
  end
167
244
 
168
245
  get '/download.csv' do
169
246
  content_type 'text/csv'
170
247
  'This, is, comma, separated' \
171
- 'Thomas, Walpole, was , here'
248
+ 'Thomas, Walpole, was , here'
249
+ end
250
+
251
+ get %r{/apple-touch-.*\.png/} do
252
+ halt(404)
172
253
  end
173
254
 
174
255
  get '/:view' do |view|
175
- erb view.to_sym, locals: { referrer: request.referrer }
256
+ view_template = "#{__dir__}/views/#{view}.erb"
257
+ has_layout = File.exist?(view_template) && File.open(view_template) { |f| f.first.downcase.include?('doctype') }
258
+ erb view.to_sym, locals: { referrer: request.referrer }, layout: !has_layout
176
259
  end
177
260
 
178
261
  post '/form' do
179
- @@form_post_count += 1
180
- '<pre id="results">' + params[:form].merge('post_count' => @@form_post_count).to_yaml + '</pre>'
262
+ self.class.form_post_count += 1
263
+ %(
264
+ <pre id="content_type">#{request.content_type}</pre>
265
+ <pre id="results">#{params.fetch(:form, {}).merge('post_count' => self.class.form_post_count).to_yaml}</pre>
266
+ )
181
267
  end
182
268
 
183
269
  post '/upload_empty' do
184
270
  if params[:form][:file].nil?
185
- 'Successfully ignored empty file field.'
271
+ "Successfully ignored empty file field. Content type was #{request.content_type}"
186
272
  else
187
273
  'Something went wrong.'
188
274
  end
189
275
  end
190
276
 
191
277
  post '/upload' do
192
- begin
193
- buffer = []
194
- buffer << "Content-type: #{params.dig(:form, :document, :type)}"
195
- buffer << "File content: #{params.dig(:form, :document, :tempfile).read}"
196
- buffer.join(' | ')
197
- rescue StandardError
198
- 'No file uploaded'
199
- end
278
+ buffer = []
279
+ buffer << "Content-type: #{params.dig(:form, :document, :type)}"
280
+ buffer << "File content: #{params.dig(:form, :document, :tempfile).read}"
281
+ buffer.join(' | ')
282
+ rescue StandardError
283
+ 'No file uploaded'
200
284
  end
201
285
 
202
286
  post '/upload_multiple' do
203
- begin
204
- docs = params.dig(:form, :multiple_documents)
205
- buffer = [docs.size.to_s]
206
- docs.each do |doc|
207
- buffer << "Content-type: #{doc[:type]}"
208
- buffer << "File content: #{doc[:tempfile].read}"
209
- end
210
- buffer.join(' | ')
211
- rescue StandardError
212
- 'No files uploaded'
287
+ docs = params.dig(:form, :multiple_documents)
288
+ buffer = [docs.size.to_s]
289
+ docs.each do |doc|
290
+ buffer << "Content-type: #{doc[:type]}"
291
+ buffer << "File content: #{doc[:tempfile].read}"
213
292
  end
293
+ buffer.join(' | ')
294
+ rescue StandardError
295
+ 'No files uploaded'
214
296
  end
297
+
298
+ class << self
299
+ attr_accessor :form_post_count
300
+ end
301
+
302
+ @form_post_count = 0
215
303
  end
216
304
 
217
305
  Rack::Handler::Puma.run TestApp, Port: 8070 if $PROGRAM_NAME == __FILE__