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,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ Capybara::SpecHelper.spec '#have_ancestor' do
4
+ before do
5
+ @session.visit('/with_html')
6
+ end
7
+
8
+ it 'should assert an ancestor using the given locator' do
9
+ el = @session.find(:css, '#ancestor1')
10
+ expect(el).to have_ancestor(:css, '#ancestor2')
11
+ end
12
+
13
+ it 'should assert an ancestor even if not parent' do
14
+ el = @session.find(:css, '#child')
15
+ expect(el).to have_ancestor(:css, '#ancestor3')
16
+ end
17
+
18
+ it 'should not raise an error if there are multiple matches' do
19
+ el = @session.find(:css, '#child')
20
+ expect(el).to have_ancestor(:css, 'div')
21
+ end
22
+
23
+ it 'should allow counts to be specified' do
24
+ el = @session.find(:css, '#child')
25
+
26
+ expect do
27
+ expect(el).to have_ancestor(:css, 'div').once
28
+ end.to raise_error(RSpec::Expectations::ExpectationNotMetError)
29
+
30
+ expect(el).to have_ancestor(:css, 'div').exactly(3).times
31
+ end
32
+ end
33
+
34
+ Capybara::SpecHelper.spec '#have_no_ancestor' do
35
+ before do
36
+ @session.visit('/with_html')
37
+ end
38
+
39
+ it 'should assert no matching ancestor' do
40
+ el = @session.find(:css, '#ancestor1')
41
+ expect(el).to have_no_ancestor(:css, '#child')
42
+ expect(el).to have_no_ancestor(:css, '#ancestor1_sibiling')
43
+ expect(el).not_to have_ancestor(:css, '#child')
44
+ expect(el).not_to have_ancestor(:css, '#ancestor1_sibiling')
45
+ end
46
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ Capybara::SpecHelper.spec '#have_any_of_selectors' do
4
+ before do
5
+ @session.visit('/with_html')
6
+ end
7
+
8
+ it 'should be true if any of the given selectors are on the page' do
9
+ expect(@session).to have_any_of_selectors(:css, 'p a#foo', 'h2#blah', 'h2#h2two')
10
+ end
11
+
12
+ it 'should be false if none of the given selectors are not on the page' do
13
+ expect do
14
+ expect(@session).to have_any_of_selectors(:css, 'span a#foo', 'h2#h2nope', 'h2#h2one_no')
15
+ end.to raise_error ::RSpec::Expectations::ExpectationNotMetError
16
+ end
17
+
18
+ it 'should use default selector' do
19
+ Capybara.default_selector = :css
20
+ expect(@session).to have_any_of_selectors('p a#foo', 'h2#h2two', 'a#not_on_page')
21
+ expect do
22
+ expect(@session).to have_any_of_selectors('p a#blah', 'h2#h2three')
23
+ end.to raise_error ::RSpec::Expectations::ExpectationNotMetError
24
+ end
25
+ end
@@ -39,6 +39,14 @@ Capybara::SpecHelper.spec '#has_button?' do
39
39
  expect(@session).to have_button('awe123', type: 'submit')
40
40
  expect(@session).not_to have_button('awe123', type: 'reset')
41
41
  end
42
+
43
+ it 'should be true for role=button when enable_aria_role: true' do
44
+ expect(@session).to have_button('ARIA button', enable_aria_role: true)
45
+ end
46
+
47
+ it 'should be false for role=button when enable_aria_role: false' do
48
+ expect(@session).not_to have_button('ARIA button', enable_aria_role: false)
49
+ end
42
50
  end
43
51
 
44
52
  Capybara::SpecHelper.spec '#has_no_button?' do
@@ -66,4 +74,12 @@ Capybara::SpecHelper.spec '#has_no_button?' do
66
74
  it 'should be false for disabled buttons if disabled: false' do
67
75
  expect(@session).to have_no_button('Disabled button', disabled: false)
68
76
  end
77
+
78
+ it 'should be true for role=button when enable_aria_role: false' do
79
+ expect(@session).to have_no_button('ARIA button', enable_aria_role: false)
80
+ end
81
+
82
+ it 'should be false for role=button when enable_aria_role: true' do
83
+ expect(@session).not_to have_no_button('ARIA button', enable_aria_role: true)
84
+ end
69
85
  end
@@ -10,10 +10,11 @@ Capybara::SpecHelper.spec '#has_css?' do
10
10
  expect(@session).to have_css('p a#foo')
11
11
  end
12
12
 
13
- it 'should take a symbol as the selector' do
13
+ it 'should warn when passed a symbol' do
14
14
  # This was never a specifically accepted format but it has worked for a
15
- # lot of versions. Probably should keep it until at least 4.0
16
- # TODO: consider not supporting symbol for the CSS selector.
15
+ # lot of versions.
16
+ # TODO: Remove in 4.0
17
+ expect_any_instance_of(Kernel).to receive(:warn) # rubocop:disable RSpec/AnyInstance
17
18
  expect(@session).to have_css(:p)
18
19
  end
19
20
 
@@ -23,10 +24,75 @@ Capybara::SpecHelper.spec '#has_css?' do
23
24
  expect(@session).not_to have_css('p.nosuchclass')
24
25
  end
25
26
 
26
- it 'should respect scopes' do
27
- @session.within "//p[@id='first']" do
28
- expect(@session).to have_css('a#foo')
29
- expect(@session).not_to have_css('a#red')
27
+ it 'should support :id option' do
28
+ expect(@session).to have_css('h2', id: 'h2one')
29
+ expect(@session).to have_css('h2')
30
+ expect(@session).to have_css('h2', id: /h2o/)
31
+ expect(@session).to have_css('li', id: /john|paul/)
32
+ end
33
+
34
+ it 'should support :class option' do
35
+ expect(@session).to have_css('li', class: 'guitarist')
36
+ expect(@session).to have_css('li', class: /guitar/)
37
+ expect(@session).to have_css('li', class: /guitar|drummer/)
38
+ expect(@session).to have_css('li', class: %w[beatle guitarist])
39
+ expect(@session).to have_css('li', class: /.*/)
40
+ end
41
+
42
+ context ':style option' do
43
+ it 'should support String' do
44
+ expect(@session).to have_css('p', style: 'line-height: 25px;')
45
+
46
+ expect do
47
+ expect(@session).to have_css('p', style: 'display: not_valid')
48
+ end.to raise_error(RSpec::Expectations::ExpectationNotMetError, /style attribute "display: not_valid"/)
49
+ end
50
+
51
+ it 'should support Regexp' do
52
+ expect(@session).to have_css('p', style: /-height: 2/)
53
+
54
+ expect do
55
+ expect(@session).to have_css('p', style: /not_valid/)
56
+ end.to raise_error(RSpec::Expectations::ExpectationNotMetError, %r{style attribute matching /not_valid/})
57
+ end
58
+
59
+ it 'should support Hash', requires: [:css] do
60
+ expect(@session).to have_css('p', style: { 'line-height': '25px' })
61
+
62
+ expect do
63
+ expect(@session).to have_css('p', style: { 'line-height': '30px' })
64
+ end.to raise_error(RSpec::Expectations::ExpectationNotMetError, /with styles \{:"line-height"=>"30px"\}/)
65
+ end
66
+ end
67
+
68
+ it 'should support case insensitive :class and :id options' do
69
+ expect(@session).to have_css('li', class: /UiTaRI/i)
70
+ expect(@session).to have_css('h2', id: /2ON/i)
71
+ end
72
+
73
+ context 'when scoped' do
74
+ it 'should look in the scope' do
75
+ @session.within "//p[@id='first']" do
76
+ expect(@session).to have_css('a#foo')
77
+ expect(@session).not_to have_css('a#red')
78
+ end
79
+ end
80
+
81
+ it 'should be able to generate an error message if the scope is a sibling' do
82
+ el = @session.find(:css, '#first')
83
+ @session.within el.sibling(:css, '#second') do
84
+ expect do
85
+ expect(@session).to have_css('a#not_on_page')
86
+ end.to raise_error(/there were no matches/)
87
+ end
88
+ end
89
+
90
+ it 'should be able to generate an error message if the scope is a sibling from XPath' do
91
+ el = @session.find(:css, '#first').find(:xpath, './following-sibling::*[1]') do
92
+ expect do
93
+ expect(el).to have_css('a#not_on_page')
94
+ end.to raise_error(/there were no matches/)
95
+ end
30
96
  end
31
97
  end
32
98
 
@@ -48,13 +114,20 @@ Capybara::SpecHelper.spec '#has_css?' do
48
114
  end
49
115
 
50
116
  context 'with predicates_wait == false' do
51
- it 'should not wait for content to appear', requires: [:js] do
117
+ before do
52
118
  Capybara.predicates_wait = false
53
- Capybara.default_max_wait_time = 2
119
+ Capybara.default_max_wait_time = 5
54
120
  @session.visit('/with_js')
55
121
  @session.click_link('Click me')
122
+ end
123
+
124
+ it 'should not wait for content to appear', requires: [:js] do
56
125
  expect(@session.has_css?("input[type='submit'][value='New Here']")).to be false
57
126
  end
127
+
128
+ it 'should should the default wait time if true is passed for :wait', requires: [:js] do
129
+ expect(@session.has_css?("input[type='submit'][value='New Here']", wait: true)).to be true
130
+ end
58
131
  end
59
132
 
60
133
  context 'with between' do
@@ -74,12 +147,24 @@ Capybara::SpecHelper.spec '#has_css?' do
74
147
  context 'with count' do
75
148
  it 'should be true if the content occurs the given number of times' do
76
149
  expect(@session).to have_css('p', count: 3)
150
+ expect(@session).to have_css('p').exactly(3).times
77
151
  expect(@session).to have_css('p a#foo', count: 1)
152
+ expect(@session).to have_css('p a#foo').once
78
153
  expect(@session).to have_css('p a.doesnotexist', count: 0)
154
+ expect(@session).to have_css('li', class: /guitar|drummer/, count: 4)
155
+ expect(@session).to have_css('li', id: /john|paul/, class: /guitar|drummer/, count: 2)
156
+ expect(@session).to have_css('li', class: %w[beatle guitarist], count: 2)
157
+ expect(@session).to have_css('p', style: 'line-height: 25px;', count: 1)
158
+ expect(@session).to have_css('p', style: /-height: 2/, count: 1)
159
+ end
160
+
161
+ it 'should be true if the content occurs the given number of times in CSS processing drivers', requires: [:css] do
162
+ expect(@session).to have_css('p', style: { 'line-height': '25px' }, count: 1)
79
163
  end
80
164
 
81
165
  it 'should be false if the content occurs a different number of times than the given' do
82
166
  expect(@session).not_to have_css('p', count: 6)
167
+ expect(@session).not_to have_css('p').exactly(5).times
83
168
  expect(@session).not_to have_css('p a#foo', count: 2)
84
169
  expect(@session).not_to have_css('p a.doesnotexist', count: 1)
85
170
  end
@@ -94,14 +179,16 @@ Capybara::SpecHelper.spec '#has_css?' do
94
179
  it 'should be true when content occurs same or fewer times than given' do
95
180
  expect(@session).to have_css('h2.head', maximum: 5) # edge case
96
181
  expect(@session).to have_css('h2', maximum: 10)
182
+ expect(@session).to have_css('h2').at_most(10).times
97
183
  expect(@session).to have_css('p a.doesnotexist', maximum: 1)
98
184
  expect(@session).to have_css('p a.doesnotexist', maximum: 0)
99
185
  end
100
186
 
101
187
  it 'should be false when content occurs more times than given' do
102
- expect(@session).not_to have_css('h2.head', maximum: 4) # edge case
103
- expect(@session).not_to have_css('h2', maximum: 3)
104
- expect(@session).not_to have_css('p', maximum: 1)
188
+ # expect(@session).not_to have_css('h2.head', maximum: 4) # edge case
189
+ # expect(@session).not_to have_css('h2', maximum: 3)
190
+ expect(@session).not_to have_css('h2').at_most(3).times
191
+ # expect(@session).not_to have_css('p', maximum: 1)
105
192
  end
106
193
 
107
194
  it 'should coerce maximum to an integer' do
@@ -114,12 +201,14 @@ Capybara::SpecHelper.spec '#has_css?' do
114
201
  it 'should be true when content occurs same or more times than given' do
115
202
  expect(@session).to have_css('h2.head', minimum: 5) # edge case
116
203
  expect(@session).to have_css('h2', minimum: 3)
204
+ expect(@session).to have_css('h2').at_least(2).times
117
205
  expect(@session).to have_css('p a.doesnotexist', minimum: 0)
118
206
  end
119
207
 
120
208
  it 'should be false when content occurs fewer times than given' do
121
209
  expect(@session).not_to have_css('h2.head', minimum: 6) # edge case
122
210
  expect(@session).not_to have_css('h2', minimum: 8)
211
+ expect(@session).not_to have_css('h2').at_least(8).times
123
212
  expect(@session).not_to have_css('p', minimum: 10)
124
213
  expect(@session).not_to have_css('p a.doesnotexist', minimum: 1)
125
214
  end
@@ -142,6 +231,27 @@ Capybara::SpecHelper.spec '#has_css?' do
142
231
  end
143
232
  end
144
233
 
234
+ context 'with spatial requirements', requires: [:spatial] do
235
+ before do
236
+ @session.visit('/spatial')
237
+ end
238
+
239
+ let :center do
240
+ @session.find(:css, '.center')
241
+ end
242
+
243
+ it 'accepts spatial options' do
244
+ expect(@session).to have_css('div', above: center).thrice
245
+ expect(@session).to have_css('div', above: center, right_of: center).once
246
+ end
247
+
248
+ it 'supports spatial sugar' do
249
+ expect(@session).to have_css('div').left_of(center).thrice
250
+ expect(@session).to have_css('div').below(center).right_of(center).once
251
+ expect(@session).to have_css('div').near(center).exactly(8).times
252
+ end
253
+ end
254
+
145
255
  it 'should allow escapes in the CSS selector' do
146
256
  expect(@session).to have_css('p[data-random="abc\\\\def"]')
147
257
  expect(@session).to have_css("p[data-random='#{Capybara::Selector::CSS.escape('abc\def')}']")
@@ -81,7 +81,8 @@ Capybara::SpecHelper.spec '#has_current_path?' do
81
81
  end
82
82
 
83
83
  it 'should not raise an exception if the current_url is nil' do
84
- allow_any_instance_of(Capybara::Session).to receive(:current_url).and_return(nil)
84
+ allow(@session).to receive(:current_url).and_return(nil)
85
+ allow(@session.page).to receive(:current_url).and_return(nil) if @session.respond_to? :page
85
86
 
86
87
  # Without ignore_query option
87
88
  expect do
@@ -121,16 +122,17 @@ Capybara::SpecHelper.spec '#has_no_current_path?' do
121
122
  end
122
123
 
123
124
  it 'should not raise an exception if the current_url is nil' do
124
- allow_any_instance_of(Capybara::Session).to receive(:current_url).and_return(nil)
125
+ allow(@session).to receive(:current_url).and_return(nil)
126
+ allow(@session.page).to receive(:current_url).and_return(nil) if @session.respond_to? :page
125
127
 
126
128
  # Without ignore_query option
127
129
  expect do
128
130
  expect(@session).not_to have_current_path('/with_js')
129
- end. not_to raise_exception
131
+ end.not_to raise_exception
130
132
 
131
133
  # With ignore_query option
132
134
  expect do
133
135
  expect(@session).not_to have_current_path('/with_js', ignore_query: true)
134
- end. not_to raise_exception
136
+ end.not_to raise_exception
135
137
  end
136
138
  end
@@ -41,6 +41,39 @@ Capybara::SpecHelper.spec '#has_field' do
41
41
  expect(@session).not_to have_field('First Name', with: 'John')
42
42
  expect(@session).not_to have_field('First Name', with: /John|Paul|George|Ringo/)
43
43
  end
44
+
45
+ it 'should output filter errors if only one element matched the selector but failed the filters' do
46
+ @session.fill_in('First Name', with: 'Thomas')
47
+ expect do
48
+ expect(@session).to have_field('First Name', with: 'Jonas')
49
+ end.to raise_exception(RSpec::Expectations::ExpectationNotMetError, /Expected value to be "Jonas" but was "Thomas"/)
50
+
51
+ # native boolean node filter
52
+ expect do
53
+ expect(@session).to have_field('First Name', readonly: true)
54
+ end.to raise_exception(RSpec::Expectations::ExpectationNotMetError, /Expected readonly true but it wasn't/)
55
+
56
+ # inherited boolean node filter
57
+ expect do
58
+ expect(@session).to have_field('form_pets_cat', checked: true)
59
+ end.to raise_exception(RSpec::Expectations::ExpectationNotMetError, /Expected checked true but it wasn't/)
60
+ end
61
+ end
62
+
63
+ context 'with validation message', requires: [:html_validation] do
64
+ it 'should accept a regexp' do
65
+ @session.fill_in('form_zipcode', with: '1234')
66
+ expect(@session).to have_field('form_zipcode', validation_message: /match the requested format/)
67
+ expect(@session).not_to have_field('form_zipcode', validation_message: /random/)
68
+ end
69
+
70
+ it 'should accept a string' do
71
+ @session.fill_in('form_zipcode', with: '1234')
72
+ expect(@session).to have_field('form_zipcode', validation_message: 'Please match the requested format.')
73
+ expect(@session).not_to have_field('form_zipcode', validation_message: 'match the requested format.')
74
+ @session.fill_in('form_zipcode', with: '12345')
75
+ expect(@session).to have_field('form_zipcode', validation_message: '')
76
+ end
44
77
  end
45
78
 
46
79
  context 'with type' do
@@ -61,6 +94,10 @@ Capybara::SpecHelper.spec '#has_field' do
61
94
  expect(@session).not_to have_field('Description', type: 'email')
62
95
  expect(@session).not_to have_field('Languages', type: 'textarea')
63
96
  end
97
+
98
+ it 'it can find type="hidden" elements if explicity specified' do
99
+ expect(@session).to have_field('form[data]', with: 'TWTW', type: 'hidden')
100
+ end
64
101
  end
65
102
 
66
103
  context 'with multiple' do
@@ -72,6 +109,24 @@ Capybara::SpecHelper.spec '#has_field' do
72
109
  expect(@session).not_to have_field('Html5 Multiple Email', multiple: false)
73
110
  end
74
111
  end
112
+
113
+ context 'with valid', requires: [:js] do
114
+ it 'should be true if field is valid' do
115
+ @session.fill_in 'required', with: 'something'
116
+ @session.fill_in 'length', with: 'abcd'
117
+
118
+ expect(@session).to have_field('required', valid: true)
119
+ expect(@session).to have_field('length', valid: true)
120
+ end
121
+
122
+ it 'should be false if field is invalid' do
123
+ expect(@session).not_to have_field('required', valid: true)
124
+ expect(@session).to have_field('required', valid: false)
125
+
126
+ @session.fill_in 'length', with: 'abc'
127
+ expect(@session).not_to have_field('length', valid: true)
128
+ end
129
+ end
75
130
  end
76
131
 
77
132
  Capybara::SpecHelper.spec '#has_no_field' do
@@ -63,7 +63,7 @@ Capybara::SpecHelper.spec '#has_select?' do
63
63
  end
64
64
 
65
65
  it "should be true even when the selected option invisible, regardless of the select's visibility" do
66
- expect(@session).to have_select('Icecream', visible: false, selected: 'Chocolate')
66
+ expect(@session).to have_select('Icecream', visible: :hidden, selected: 'Chocolate')
67
67
  expect(@session).to have_select('Sorbet', selected: 'Vanilla')
68
68
  end
69
69
  end
@@ -88,7 +88,7 @@ Capybara::SpecHelper.spec '#has_select?' do
88
88
  end
89
89
 
90
90
  it "should be true even when the selected values are invisible, regardless of the select's visibility" do
91
- expect(@session).to have_select('Dessert', visible: false, with_options: %w[Pudding Tiramisu])
91
+ expect(@session).to have_select('Dessert', visible: :hidden, with_options: %w[Pudding Tiramisu])
92
92
  expect(@session).to have_select('Cake', with_selected: ['Chocolate Cake', 'Sponge Cake'])
93
93
  end
94
94
 
@@ -113,7 +113,35 @@ Capybara::SpecHelper.spec '#has_select?' do
113
113
  end
114
114
 
115
115
  it 'should be true even when the options are invisible, if the select itself is invisible' do
116
- expect(@session).to have_select('Icecream', visible: false, options: %w[Chocolate Vanilla Strawberry])
116
+ expect(@session).to have_select('Icecream', visible: :hidden, options: %w[Chocolate Vanilla Strawberry])
117
+ end
118
+ end
119
+
120
+ context 'with enabled options' do
121
+ it 'should be true if the listed options exist and are enabled' do
122
+ expect(@session).to have_select('form_title', enabled_options: %w[Mr Mrs Miss])
123
+ end
124
+
125
+ it 'should be false if the listed options do not exist' do
126
+ expect(@session).not_to have_select('form_title', enabled_options: ['Not there'])
127
+ end
128
+
129
+ it 'should be false if the listed option exists but is not enabled' do
130
+ expect(@session).not_to have_select('form_title', enabled_options: %w[Mr Mrs Miss Other])
131
+ end
132
+ end
133
+
134
+ context 'with disabled options' do
135
+ it 'should be true if the listed options exist and are disabled' do
136
+ expect(@session).to have_select('form_title', disabled_options: ['Other'])
137
+ end
138
+
139
+ it 'should be false if the listed options do not exist' do
140
+ expect(@session).not_to have_select('form_title', disabled_options: ['Not there'])
141
+ end
142
+
143
+ it 'should be false if the listed option exists but is not disabled' do
144
+ expect(@session).not_to have_select('form_title', disabled_options: %w[Other Mrs])
117
145
  end
118
146
  end
119
147
 
@@ -130,7 +158,7 @@ Capybara::SpecHelper.spec '#has_select?' do
130
158
  end
131
159
 
132
160
  it 'should be true even when the options are invisible, if the select itself is invisible' do
133
- expect(@session).to have_select('Icecream', visible: false, with_options: %w[Vanilla Strawberry])
161
+ expect(@session).to have_select('Icecream', visible: :hidden, with_options: %w[Vanilla Strawberry])
134
162
  end
135
163
  end
136
164
 
@@ -276,7 +304,7 @@ Capybara::SpecHelper.spec '#has_no_select?' do
276
304
  it 'should support locator-less usage' do
277
305
  expect(@session.has_no_select?(with_options: %w[Norway Sweden Finland Latvia])).to eq true
278
306
  expect(@session).to have_no_select(with_options: ['New London'])
279
- expect(@session.has_no_select?(with_selected: ['Boxers'])).to eq true
280
- expect(@session).to have_no_select(with_selected: %w[Commando Boxers])
307
+ expect(@session.has_no_select?(id: 'form_underwear', with_selected: ['Boxers'])).to eq true
308
+ expect(@session).to have_no_select(id: 'form_underwear', with_selected: %w[Commando Boxers])
281
309
  end
282
310
  end
@@ -56,16 +56,17 @@ Capybara::SpecHelper.spec '#has_selector?' do
56
56
  context 'with text' do
57
57
  it 'should discard all matches where the given string is not contained' do
58
58
  expect(@session).to have_selector('//p//a', text: 'Redirect', count: 1)
59
+ expect(@session).to have_selector(:css, 'p a', text: 'Redirect', count: 1)
59
60
  expect(@session).not_to have_selector('//p', text: 'Doesnotexist')
60
61
  end
61
62
 
62
63
  it 'should respect visibility setting' do
63
- expect(@session).to have_selector(:id, 'hidden-text', text: 'Some of this text is hidden!', visible: false)
64
- expect(@session).not_to have_selector(:id, 'hidden-text', text: 'Some of this text is hidden!', visible: true)
64
+ expect(@session).to have_selector(:id, 'hidden-text', text: 'Some of this text is hidden!', visible: :all)
65
+ expect(@session).not_to have_selector(:id, 'hidden-text', text: 'Some of this text is hidden!', visible: :visible)
65
66
  Capybara.ignore_hidden_elements = false
66
- expect(@session).to have_selector(:id, 'hidden-text', text: 'Some of this text is hidden!', visible: false)
67
+ expect(@session).to have_selector(:id, 'hidden-text', text: 'Some of this text is hidden!', visible: :all)
67
68
  Capybara.visible_text_only = true
68
- expect(@session).not_to have_selector(:id, 'hidden-text', text: 'Some of this text is hidden!', visible: true)
69
+ expect(@session).not_to have_selector(:id, 'hidden-text', text: 'Some of this text is hidden!', visible: :visible)
69
70
  end
70
71
 
71
72
  it 'should discard all matches where the given regexp is not matched' do
@@ -191,5 +192,11 @@ Capybara::SpecHelper.spec '#has_no_selector?' do
191
192
  expect(@session).not_to have_no_selector('//p//a', text: /re[dab]i/i, count: 1)
192
193
  expect(@session).to have_no_selector('//p//a', text: /Red$/)
193
194
  end
195
+
196
+ it 'should error when matching element exists' do
197
+ expect do
198
+ expect(@session).to have_no_selector('//h2', text: 'Header Class Test Five')
199
+ end.to raise_error RSpec::Expectations::ExpectationNotMetError
200
+ end
194
201
  end
195
202
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ Capybara::SpecHelper.spec '#have_sibling' do
4
+ before do
5
+ @session.visit('/with_html')
6
+ end
7
+
8
+ it 'should assert a prior sibling element using the given locator' do
9
+ el = @session.find(:css, '#mid_sibling')
10
+ expect(el).to have_sibling(:css, '#pre_sibling')
11
+ end
12
+
13
+ it 'should assert a following sibling element using the given locator' do
14
+ el = @session.find(:css, '#mid_sibling')
15
+ expect(el).to have_sibling(:css, '#post_sibling')
16
+ end
17
+
18
+ it 'should not raise an error if there are multiple matches' do
19
+ el = @session.find(:css, '#mid_sibling')
20
+ expect(el).to have_sibling(:css, 'div')
21
+ end
22
+
23
+ it 'should allow counts to be specified' do
24
+ el = @session.find(:css, '#mid_sibling')
25
+
26
+ expect(el).to have_sibling(:css, 'div').exactly(2).times
27
+ expect do
28
+ expect(el).to have_sibling(:css, 'div').once
29
+ end.to raise_error(RSpec::Expectations::ExpectationNotMetError)
30
+ end
31
+ end
32
+
33
+ Capybara::SpecHelper.spec '#have_no_sibling' do
34
+ before do
35
+ @session.visit('/with_html')
36
+ end
37
+
38
+ it 'should assert no matching sibling' do
39
+ el = @session.find(:css, '#mid_sibling')
40
+ expect(el).to have_no_sibling(:css, '#not_a_sibling')
41
+ expect(el).not_to have_sibling(:css, '#not_a_sibling')
42
+ end
43
+
44
+ it 'should raise if there are matching siblings' do
45
+ el = @session.find(:css, '#mid_sibling')
46
+ expect do
47
+ expect(el).to have_no_sibling(:css, '#pre_sibling')
48
+ end.to raise_error(RSpec::Expectations::ExpectationNotMetError)
49
+ end
50
+ end