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
data/spec/rspec_spec.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # rubocop:disable RSpec/MultipleDescribes
4
+
3
5
  require 'spec_helper'
4
6
 
5
7
  RSpec.describe 'capybara/rspec' do
@@ -13,7 +15,7 @@ RSpec.describe 'capybara/rspec' do
13
15
  expect(self.class.ancestors).to include Capybara::RSpecMatcherProxies
14
16
  end
15
17
 
16
- context 'resetting session' do
18
+ context 'resetting session', order: :defined do
17
19
  it 'sets a cookie in one example...' do
18
20
  visit('/set_cookie')
19
21
  expect(page.body).to include('Cookie set to test_cookie')
@@ -25,7 +27,7 @@ RSpec.describe 'capybara/rspec' do
25
27
  end
26
28
  end
27
29
 
28
- context 'setting the current driver' do
30
+ context 'setting the current driver', order: :defined do
29
31
  it 'sets the current driver in one example...' do
30
32
  Capybara.current_driver = :selenium
31
33
  end
@@ -35,7 +37,7 @@ RSpec.describe 'capybara/rspec' do
35
37
  end
36
38
  end
37
39
 
38
- it 'switches to the javascript driver when giving it as metadata', js: true do
40
+ it 'switches to the javascript driver when giving it as metadata', :js do
39
41
  expect(Capybara.current_driver).to eq(Capybara.javascript_driver)
40
42
  end
41
43
 
@@ -43,7 +45,7 @@ RSpec.describe 'capybara/rspec' do
43
45
  expect(Capybara.current_driver).to eq(:culerity)
44
46
  end
45
47
 
46
- context '#all' do
48
+ describe '#all' do
47
49
  it 'allows access to the Capybara finder' do
48
50
  visit('/with_html')
49
51
  found = all(:css, 'h2') { |element| element[:class] == 'head' }
@@ -57,7 +59,7 @@ RSpec.describe 'capybara/rspec' do
57
59
  end
58
60
  end
59
61
 
60
- context '#within' do
62
+ describe '#within' do
61
63
  it 'allows access to the Capybara scoper' do
62
64
  visit('/with_html')
63
65
  expect do
@@ -75,40 +77,38 @@ RSpec.describe 'capybara/rspec' do
75
77
 
76
78
  context 'Type: Other', type: :other do
77
79
  context 'when RSpec::Matchers is included after Capybara::DSL' do
78
- before do
79
- class DSLMatchersTest
80
+ let(:test_class_instance) do
81
+ Class.new do
80
82
  include Capybara::DSL
81
83
  include RSpec::Matchers
82
- end
83
-
84
- @test_class_instance = DSLMatchersTest.new
84
+ end.new
85
85
  end
86
86
 
87
- context '#all' do
87
+ describe '#all' do
88
88
  it 'allows access to the Capybara finder' do
89
- @test_class_instance.visit('/with_html')
90
- expect(@test_class_instance.all(:css, 'h2.head').size).to eq(5)
89
+ test_class_instance.visit('/with_html')
90
+ expect(test_class_instance.all(:css, 'h2.head').size).to eq(5)
91
91
  end
92
92
 
93
93
  it 'allows access to the RSpec matcher' do
94
- @test_class_instance.visit('/with_html')
94
+ test_class_instance.visit('/with_html')
95
95
  strings = %w[test1 test2]
96
- expect(strings).to @test_class_instance.all(be_a(String))
96
+ expect(strings).to test_class_instance.all(be_a(String))
97
97
  end
98
98
  end
99
99
 
100
- context '#within' do
100
+ describe '#within' do
101
101
  it 'allows access to the Capybara scoper' do
102
- @test_class_instance.visit('/with_html')
102
+ test_class_instance.visit('/with_html')
103
103
  expect do
104
- @test_class_instance.within(:css, '#does_not_exist') { @test_class_instance.click_link 'Go to simple' }
104
+ test_class_instance.within(:css, '#does_not_exist') { test_class_instance.click_link 'Go to simple' }
105
105
  end.to raise_error(Capybara::ElementNotFound)
106
106
  end
107
107
 
108
108
  it 'allows access to the RSpec matcher' do
109
- @test_class_instance.visit('/with_html')
109
+ test_class_instance.visit('/with_html')
110
110
  # This reads terribly, but must call #within
111
- expect(@test_class_instance.find(:css, 'span.number').text.to_i).to @test_class_instance.within(1).of(41)
111
+ expect(test_class_instance.find(:css, 'span.number').text.to_i).to test_class_instance.within(1).of(41)
112
112
  end
113
113
  end
114
114
 
@@ -124,7 +124,7 @@ RSpec.describe 'capybara/rspec' do
124
124
  it 'can be called with `not_to`' do
125
125
  # This test is for a bug in jruby where `super` isn't defined correctly - https://github.com/jruby/jruby/issues/4678
126
126
  # Reported in https://github.com/teamcapybara/capybara/issues/2115
127
- @test_class_instance.instance_eval do
127
+ test_class_instance.instance_eval do
128
128
  expect do
129
129
  expect(true).not_to only_match_matcher(false) # rubocop:disable RSpec/ExpectActual
130
130
  end.not_to raise_error
@@ -145,3 +145,5 @@ feature 'Feature DSL' do
145
145
  expect(page.body).to include('Another World')
146
146
  end
147
147
  end
148
+
149
+ # rubocop:enable RSpec/MultipleDescribes
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'selenium-webdriver'
5
+
6
+ require 'sauce_whisk'
7
+ # require 'shared_selenium_session'
8
+ # require 'shared_selenium_node'
9
+ # require 'rspec/shared_spec_matchers'
10
+
11
+ Capybara.register_driver :sauce_chrome do |app|
12
+ options = {
13
+ selenium_version: '4.8.0',
14
+ platform: 'macOS 10.12',
15
+ browser_name: 'chrome',
16
+ version: '65.0',
17
+ name: 'Capybara test',
18
+ build: ENV.fetch('TRAVIS_REPO_SLUG', "Ruby-RSpec-Selenium: Local-#{Time.now.to_i}"),
19
+ username: ENV.fetch('SAUCE_USERNAME', nil),
20
+ access_key: ENV.fetch('SAUCE_ACCESS_KEY', nil)
21
+ }
22
+
23
+ options.delete(:browser_name)
24
+
25
+ capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(options)
26
+ url = 'https://ondemand.saucelabs.com:443/wd/hub'
27
+
28
+ Capybara::Selenium::Driver.new(app,
29
+ browser: :remote, url: url,
30
+ capabilities: capabilities,
31
+ options: Selenium::WebDriver::Chrome::Options.new(args: ['']))
32
+ end
33
+
34
+ CHROME_REMOTE_DRIVER = :sauce_chrome
35
+
36
+ module TestSessions
37
+ Chrome = Capybara::Session.new(CHROME_REMOTE_DRIVER, TestApp)
38
+ end
39
+
40
+ skipped_tests = %i[response_headers status_code trigger download]
41
+
42
+ Capybara::SpecHelper.run_specs TestSessions::Chrome, CHROME_REMOTE_DRIVER.to_s, capybara_skip: skipped_tests do |example|
43
+ end
@@ -3,9 +3,10 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  RSpec.describe Capybara do
6
+ include Capybara::RSpecMatchers
6
7
  describe 'Selectors' do
7
8
  let :string do
8
- Capybara.string <<-STRING
9
+ described_class.string <<-STRING
9
10
  <html>
10
11
  <head>
11
12
  <title>selectors</title>
@@ -42,18 +43,39 @@ RSpec.describe Capybara do
42
43
  <table>
43
44
  <tr><td></td></tr>
44
45
  </table>
46
+ <table id="rows">
47
+ <tr>
48
+ <td>A</td><td>B</td><td>C</td>
49
+ </tr>
50
+ <tr>
51
+ <td>D</td><td>E</td><td>F</td>
52
+ </tr>
53
+ </table>
54
+ <table id="cols">
55
+ <tbody>
56
+ <tr>
57
+ <td>A</td><td>D</td>
58
+ </tr>
59
+ <tr>
60
+ <td>B</td><td>E</td>
61
+ </tr>
62
+ <tr>
63
+ <td>C</td><td>F</td>
64
+ </tr>
65
+ </tbody>
66
+ </table>
45
67
  </body>
46
68
  </html>
47
69
  STRING
48
70
  end
49
71
 
50
72
  before do
51
- Capybara.add_selector :custom_selector do
73
+ described_class.add_selector :custom_selector do
52
74
  css { |css_class| "div.#{css_class}" }
53
75
  node_filter(:not_empty, boolean: true, default: true, skip_if: :all) { |node, value| value ^ (node.text == '') }
54
76
  end
55
77
 
56
- Capybara.add_selector :custom_css_selector do
78
+ described_class.add_selector :custom_css_selector do
57
79
  css(:name, :other_name) do |selector, name: nil, **|
58
80
  selector ||= ''
59
81
  selector += "[name='#{name}']" if name
@@ -73,7 +95,7 @@ RSpec.describe Capybara do
73
95
  end
74
96
  end
75
97
 
76
- Capybara.add_selector :custom_xpath_selector do
98
+ described_class.add_selector :custom_xpath_selector do
77
99
  xpath(:valid1, :valid2) { |selector| selector }
78
100
  match { |value| value == 'match_me' }
79
101
  end
@@ -81,7 +103,7 @@ RSpec.describe Capybara do
81
103
 
82
104
  it 'supports `filter` as an alias for `node_filter`' do
83
105
  expect do
84
- Capybara.add_selector :filter_alias_selector do
106
+ described_class.add_selector :filter_alias_selector do
85
107
  css { |_unused| 'div' }
86
108
  filter(:something) { |_node, _value| true }
87
109
  end
@@ -89,13 +111,13 @@ RSpec.describe Capybara do
89
111
  end
90
112
 
91
113
  describe 'adding a selector' do
92
- it 'can set default visiblity' do
93
- Capybara.add_selector :hidden_field do
114
+ it 'can set default visibility' do
115
+ described_class.add_selector :hidden_field do
94
116
  visible :hidden
95
117
  css { |_sel| 'input[type="hidden"]' }
96
118
  end
97
119
 
98
- expect(string).to have_no_css('input[type="hidden"]')
120
+ expect(string).to have_no_css('input[type="hidden"]') # rubocop:disable Capybara/SpecificMatcher
99
121
  expect(string).to have_selector(:hidden_field)
100
122
  end
101
123
  end
@@ -104,7 +126,7 @@ RSpec.describe Capybara do
104
126
  it 'allows modifying a selector' do
105
127
  el = string.find(:custom_selector, 'aa')
106
128
  expect(el.tag_name).to eq 'div'
107
- Capybara.modify_selector :custom_selector do
129
+ described_class.modify_selector :custom_selector do
108
130
  css { |css_class| "h1.#{css_class}" }
109
131
  end
110
132
  el = string.find(:custom_selector, 'aa')
@@ -112,7 +134,7 @@ RSpec.describe Capybara do
112
134
  end
113
135
 
114
136
  it "doesn't change existing filters" do
115
- Capybara.modify_selector :custom_selector do
137
+ described_class.modify_selector :custom_selector do
116
138
  css { |css_class| "p.#{css_class}" }
117
139
  end
118
140
  expect(string).to have_selector(:custom_selector, 'bb', count: 1)
@@ -137,31 +159,34 @@ RSpec.describe Capybara do
137
159
  end
138
160
 
139
161
  it 'returns nil if no match' do
140
- expect(Capybara::Selector.for('nothing')).to be nil
162
+ expect(Capybara::Selector.for('nothing')).to be_nil
141
163
  end
142
164
  end
143
165
 
144
166
  describe 'xpath' do
145
167
  it 'uses filter names passed in' do
146
- selector = Capybara::Selector.new :test do
168
+ described_class.add_selector :test do
147
169
  xpath(:something, :other) { |_locator| XPath.descendant }
148
170
  end
171
+ selector = Capybara::Selector.new :test, config: nil, format: nil
149
172
 
150
173
  expect(selector.expression_filters.keys).to include(:something, :other)
151
174
  end
152
175
 
153
176
  it 'gets filter names from block if none passed to xpath method' do
154
- selector = Capybara::Selector.new :test do
177
+ described_class.add_selector :test do
155
178
  xpath { |_locator, valid3:, valid4: nil| "#{valid3} #{valid4}" }
156
179
  end
180
+ selector = Capybara::Selector.new :test, config: nil, format: nil
157
181
 
158
182
  expect(selector.expression_filters.keys).to include(:valid3, :valid4)
159
183
  end
160
184
 
161
185
  it 'ignores block parameters if names passed in' do
162
- selector = Capybara::Selector.new :test do
186
+ described_class.add_selector :test do
163
187
  xpath(:valid1) { |_locator, valid3:, valid4: nil| "#{valid3} #{valid4}" }
164
188
  end
189
+ selector = Capybara::Selector.new :test, config: nil, format: nil
165
190
 
166
191
  expect(selector.expression_filters.keys).to include(:valid1)
167
192
  expect(selector.expression_filters.keys).not_to include(:valid3, :valid4)
@@ -181,25 +206,28 @@ RSpec.describe Capybara do
181
206
  end
182
207
 
183
208
  it 'uses filter names passed in' do
184
- selector = Capybara::Selector.new :text do
209
+ described_class.add_selector :test do
185
210
  css(:name, :other_name) { |_locator| '' }
186
211
  end
212
+ selector = Capybara::Selector.new :test, config: nil, format: nil
187
213
 
188
214
  expect(selector.expression_filters.keys).to include(:name, :other_name)
189
215
  end
190
216
 
191
217
  it 'gets filter names from block if none passed to css method' do
192
- selector = Capybara::Selector.new :test do
218
+ described_class.add_selector :test do
193
219
  css { |_locator, valid3:, valid4: nil| "#{valid3} #{valid4}" }
194
220
  end
221
+ selector = Capybara::Selector.new :test, config: nil, format: nil
195
222
 
196
223
  expect(selector.expression_filters.keys).to include(:valid3, :valid4)
197
224
  end
198
225
 
199
226
  it 'ignores block parameters if names passed in' do
200
- selector = Capybara::Selector.new :test do
227
+ described_class.add_selector :test do
201
228
  css(:valid1) { |_locator, valid3:, valid4: nil| "#{valid3} #{valid4}" }
202
229
  end
230
+ selector = Capybara::Selector.new :test, config: nil, format: nil
203
231
 
204
232
  expect(selector.expression_filters.keys).to include(:valid1)
205
233
  expect(selector.expression_filters.keys).not_to include(:valid3, :valid4)
@@ -299,9 +327,23 @@ RSpec.describe Capybara do
299
327
  expect(string.find(:custom_xpath_selector, './/div', class: /dOm WoR/i)[:id]).to eq 'random_words'
300
328
  end
301
329
 
302
- it 'accepts Regexp for CSS base selectors' do
330
+ it 'accepts Regexp for CSS based selectors' do
303
331
  expect(string.find(:custom_css_selector, 'div', class: /random/)[:id]).to eq 'random_words'
304
332
  end
333
+
334
+ it 'accepts Regexp for individual class names for XPath based selectors' do
335
+ expect(string.find(:custom_xpath_selector, './/div', class: [/random/, 'some'])[:id]).to eq 'random_words'
336
+ expect(string.find(:custom_xpath_selector, './/div', class: [/om/, /wor/])[:id]).to eq 'random_words'
337
+ expect { string.find(:custom_xpath_selector, './/div', class: [/not/, /wor/]) }.to raise_error(Capybara::ElementNotFound)
338
+ expect { string.find(:custom_xpath_selector, './/div', class: [/dom wor/]) }.to raise_error(Capybara::ElementNotFound)
339
+ end
340
+
341
+ it 'accepts Regexp for individual class names for CSS based selectors' do
342
+ expect(string.find(:custom_css_selector, 'div', class: [/random/])[:id]).to eq 'random_words'
343
+ expect(string.find(:custom_css_selector, 'div', class: [/om/, /wor/, 'some'])[:id]).to eq 'random_words'
344
+ expect { string.find(:custom_css_selector, 'div', class: [/not/, /wor/]) }.to raise_error(Capybara::ElementNotFound)
345
+ expect { string.find(:custom_css_selector, 'div', class: [/dom wor/]) }.to raise_error(Capybara::ElementNotFound)
346
+ end
305
347
  end
306
348
 
307
349
  context 'with :style option' do
@@ -429,7 +471,7 @@ RSpec.describe Capybara do
429
471
  end
430
472
 
431
473
  it 'includes wildcarded keys in description' do
432
- expect { string.find(:element, 'input', not_there: 'bad', presence: true, absence: false, count: 1) }
474
+ expect { string.all(:element, 'input', not_there: 'bad', presence: true, absence: false, count: 1) }
433
475
  .to(raise_error do |e|
434
476
  expect(e).to be_a(Capybara::ElementNotFound)
435
477
  expect(e.message).to include 'not_there => bad'
@@ -449,14 +491,18 @@ RSpec.describe Capybara do
449
491
  end
450
492
 
451
493
  describe ':link_or_button selector' do
452
- around(:all) do |example|
453
- Capybara.modify_selector(:link_or_button) do
494
+ around do |example|
495
+ described_class.modify_selector(:link_or_button) do
454
496
  expression_filter(:random) { |xpath, _| xpath } # do nothing filter
455
497
  end
456
498
  example.run
457
499
  Capybara::Selector[:link_or_button].expression_filters.delete(:random)
458
500
  end
459
501
 
502
+ it 'should not find links when disabled == true' do
503
+ expect(string.all(:link_or_button, disabled: true).size).to eq 0
504
+ end
505
+
460
506
  context 'when modified' do
461
507
  it 'should still work' do
462
508
  filter = Capybara::Selector[:link_or_button].expression_filters[:random]
@@ -467,6 +513,16 @@ RSpec.describe Capybara do
467
513
  end
468
514
  end
469
515
  end
516
+
517
+ describe ':table selector' do
518
+ it 'finds by rows' do
519
+ expect(string.find(:table, with_rows: [%w[D E F]])[:id]).to eq 'rows'
520
+ end
521
+
522
+ it 'finds by columns' do
523
+ expect(string.find(:table, with_cols: [%w[A B C]])[:id]).to eq 'cols'
524
+ end
525
+ end
470
526
  end
471
527
  end
472
528
  end
@@ -3,26 +3,77 @@
3
3
  require 'spec_helper'
4
4
  require 'selenium-webdriver'
5
5
  require 'shared_selenium_session'
6
+ require 'shared_selenium_node'
6
7
  require 'rspec/shared_spec_matchers'
7
8
 
8
9
  CHROME_DRIVER = :selenium_chrome
9
10
 
10
- browser_options = ::Selenium::WebDriver::Chrome::Options.new
11
- browser_options.headless! if ENV['HEADLESS']
12
- browser_options.add_option(:w3c, !!ENV['W3C'])
11
+ Selenium::WebDriver::Chrome.path = '/usr/bin/google-chrome-beta' if ENV.fetch('CI', nil) && ENV.fetch('CHROME_BETA', nil)
12
+
13
+ Selenium::WebDriver.logger.ignore(:selenium_manager)
14
+
15
+ browser_options = if ENV['HEADLESS']
16
+ Selenium::WebDriver::Options.chrome(args: ['--headless=new'])
17
+ else
18
+ Selenium::WebDriver::Options.chrome
19
+ end
20
+
21
+ # Chromedriver 77 requires setting this for headless mode on linux
22
+ # Different versions of Chrome/selenium-webdriver require setting differently - jus set them all
23
+ browser_options.add_preference('download.default_directory', Capybara.save_path)
24
+ browser_options.add_preference(:download, default_directory: Capybara.save_path)
13
25
 
14
26
  Capybara.register_driver :selenium_chrome do |app|
15
- Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options, timeout: 30).tap do |driver|
27
+ version = Capybara::Selenium::Driver.load_selenium
28
+ options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
29
+ driver_options = { browser: :chrome, timeout: 30 }.tap do |opts|
30
+ opts[options_key] = browser_options
31
+ end
32
+
33
+ Capybara::Selenium::Driver.new(app, **driver_options).tap do |driver|
34
+ # Set download dir for Chrome < 77
16
35
  driver.browser.download_path = Capybara.save_path
17
36
  end
18
37
  end
19
38
 
20
39
  Capybara.register_driver :selenium_chrome_not_clear_storage do |app|
21
- chrome_options = {
22
- browser: :chrome,
23
- options: browser_options
24
- }
25
- Capybara::Selenium::Driver.new(app, chrome_options.merge(clear_local_storage: false, clear_session_storage: false))
40
+ version = Capybara::Selenium::Driver.load_selenium
41
+ options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
42
+ chrome_options = { browser: :chrome, clear_local_storage: false, clear_session_storage: false }.tap do |opts|
43
+ opts[options_key] = browser_options
44
+ end
45
+
46
+ Capybara::Selenium::Driver.new(app, **chrome_options)
47
+ end
48
+
49
+ Capybara.register_driver :selenium_chrome_not_clear_session_storage do |app|
50
+ version = Capybara::Selenium::Driver.load_selenium
51
+ options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
52
+ chrome_options = { browser: :chrome, clear_session_storage: false }.tap do |opts|
53
+ opts[options_key] = browser_options
54
+ end
55
+
56
+ Capybara::Selenium::Driver.new(app, **chrome_options)
57
+ end
58
+
59
+ Capybara.register_driver :selenium_chrome_not_clear_local_storage do |app|
60
+ version = Capybara::Selenium::Driver.load_selenium
61
+ options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
62
+ chrome_options = { browser: :chrome, clear_local_storage: false }.tap do |opts|
63
+ opts[options_key] = browser_options
64
+ end
65
+ Capybara::Selenium::Driver.new(app, **chrome_options)
66
+ end
67
+
68
+ Capybara.register_driver :selenium_driver_subclass_with_chrome do |app|
69
+ version = Capybara::Selenium::Driver.load_selenium
70
+ options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
71
+ subclass = Class.new(Capybara::Selenium::Driver)
72
+ chrome_options = { browser: :chrome, timeout: 30 }.tap do |opts|
73
+ opts[options_key] = browser_options
74
+ end
75
+
76
+ subclass.new(app, **chrome_options)
26
77
  end
27
78
 
28
79
  module TestSessions
@@ -31,42 +82,65 @@ end
31
82
 
32
83
  skipped_tests = %i[response_headers status_code trigger]
33
84
 
34
- $stdout.puts `#{Selenium::WebDriver::Chrome.driver_path} --version` if ENV['CI']
85
+ Capybara::SpecHelper.log_selenium_driver_version(Selenium::WebDriver::Chrome) if ENV['CI']
35
86
 
36
87
  Capybara::SpecHelper.run_specs TestSessions::Chrome, CHROME_DRIVER.to_s, capybara_skip: skipped_tests do |example|
37
88
  case example.metadata[:full_description]
38
89
  when /#click_link can download a file$/
39
90
  skip 'Need to figure out testing of file downloading on windows platform' if Gem.win_platform?
40
- when /Capybara::Session selenium_chrome Capybara::Window#maximize/
41
- pending "Chrome headless doesn't support maximize" if ENV['HEADLESS']
91
+ when /Capybara::Session selenium_chrome node #shadow_root should get visible text/
92
+ pending "Selenium doesn't currently support getting visible text for shadow root elements"
93
+ when /Capybara::Session selenium_chrome node #shadow_root/
94
+ skip 'Not supported with this chromedriver version' if chromedriver_lt?('96.0', @session)
42
95
  end
43
96
  end
44
97
 
45
98
  RSpec.describe 'Capybara::Session with chrome' do
46
99
  include Capybara::SpecHelper
47
- include_examples 'Capybara::Session', TestSessions::Chrome, CHROME_DRIVER
48
- include_examples Capybara::RSpecMatchers, TestSessions::Chrome, CHROME_DRIVER
100
+ ['Capybara::Session', 'Capybara::Node', Capybara::RSpecMatchers].each do |examples|
101
+ include_examples examples, TestSessions::Chrome, CHROME_DRIVER
102
+ end
49
103
 
50
104
  context 'storage' do
51
105
  describe '#reset!' do
52
106
  it 'clears storage by default' do
53
- @session = TestSessions::Chrome
54
- @session.visit('/with_js')
55
- @session.find(:css, '#set-storage').click
56
- @session.reset!
57
- @session.visit('/with_js')
58
- expect(@session.evaluate_script('Object.keys(localStorage)')).to be_empty
59
- expect(@session.evaluate_script('Object.keys(sessionStorage)')).to be_empty
107
+ session = TestSessions::Chrome
108
+ session.visit('/with_js')
109
+ session.find(:css, '#set-storage').click
110
+ session.reset!
111
+ session.visit('/with_js')
112
+ expect(session.evaluate_script('Object.keys(localStorage)')).to be_empty
113
+ expect(session.evaluate_script('Object.keys(sessionStorage)')).to be_empty
60
114
  end
61
115
 
62
116
  it 'does not clear storage when false' do
63
- @session = Capybara::Session.new(:selenium_chrome_not_clear_storage, TestApp)
64
- @session.visit('/with_js')
65
- @session.find(:css, '#set-storage').click
66
- @session.reset!
67
- @session.visit('/with_js')
68
- expect(@session.evaluate_script('Object.keys(localStorage)')).not_to be_empty
69
- expect(@session.evaluate_script('Object.keys(sessionStorage)')).not_to be_empty
117
+ session = Capybara::Session.new(:selenium_chrome_not_clear_storage, TestApp)
118
+ session.visit('/with_js')
119
+ session.find(:css, '#set-storage').click
120
+ session.reset!
121
+ session.visit('/with_js')
122
+ expect(session.evaluate_script('Object.keys(localStorage)')).not_to be_empty
123
+ expect(session.evaluate_script('Object.keys(sessionStorage)')).not_to be_empty
124
+ end
125
+
126
+ it 'can not clear session storage' do
127
+ session = Capybara::Session.new(:selenium_chrome_not_clear_session_storage, TestApp)
128
+ session.visit('/with_js')
129
+ session.find(:css, '#set-storage').click
130
+ session.reset!
131
+ session.visit('/with_js')
132
+ expect(session.evaluate_script('Object.keys(localStorage)')).to be_empty
133
+ expect(session.evaluate_script('Object.keys(sessionStorage)')).not_to be_empty
134
+ end
135
+
136
+ it 'can not clear local storage' do
137
+ session = Capybara::Session.new(:selenium_chrome_not_clear_local_storage, TestApp)
138
+ session.visit('/with_js')
139
+ session.find(:css, '#set-storage').click
140
+ session.reset!
141
+ session.visit('/with_js')
142
+ expect(session.evaluate_script('Object.keys(localStorage)')).not_to be_empty
143
+ expect(session.evaluate_script('Object.keys(sessionStorage)')).to be_empty
70
144
  end
71
145
  end
72
146
  end
@@ -79,29 +153,57 @@ RSpec.describe 'Capybara::Session with chrome' do
79
153
 
80
154
  describe 'filling in Chrome-specific date and time fields with keystrokes' do
81
155
  let(:datetime) { Time.new(1983, 6, 19, 6, 30) }
156
+ let(:session) { TestSessions::Chrome }
82
157
 
83
158
  before do
84
- @session = TestSessions::Chrome
85
- @session.visit('/form')
159
+ session.visit('/form')
86
160
  end
87
161
 
88
162
  it 'should fill in a date input with a String' do
89
- @session.fill_in('form_date', with: '06/19/1983')
90
- @session.click_button('awesome')
91
- expect(Date.parse(extract_results(@session)['date'])).to eq datetime.to_date
163
+ session.fill_in('form_date', with: '06/19/1983')
164
+ session.click_button('awesome')
165
+ expect(Date.parse(extract_results(session)['date'])).to eq datetime.to_date
92
166
  end
93
167
 
94
168
  it 'should fill in a time input with a String' do
95
- @session.fill_in('form_time', with: '06:30A')
96
- @session.click_button('awesome')
97
- results = extract_results(@session)['time']
169
+ session.fill_in('form_time', with: '06:30A')
170
+ session.click_button('awesome')
171
+ results = extract_results(session)['time']
98
172
  expect(Time.parse(results).strftime('%r')).to eq datetime.strftime('%r')
99
173
  end
100
174
 
101
175
  it 'should fill in a datetime input with a String' do
102
- @session.fill_in('form_datetime', with: "06/19/1983\t06:30A")
103
- @session.click_button('awesome')
104
- expect(Time.parse(extract_results(@session)['datetime'])).to eq datetime
176
+ session.fill_in('form_datetime', with: "06/19/1983\t06:30A")
177
+ session.click_button('awesome')
178
+ expect(Time.parse(extract_results(session)['datetime'])).to eq datetime
179
+ end
180
+ end
181
+
182
+ describe 'using subclass of selenium driver' do
183
+ it 'works' do
184
+ session = Capybara::Session.new(:selenium_driver_subclass_with_chrome, TestApp)
185
+ session.visit('/form')
186
+ expect(session).to have_current_path('/form')
187
+ end
188
+ end
189
+
190
+ describe 'log access' do
191
+ let(:logs) do
192
+ session.driver.browser.then do |chrome_driver|
193
+ chrome_driver.respond_to?(:logs) ? chrome_driver : chrome_driver.manage
194
+ end.logs
195
+ end
196
+
197
+ it 'does not error getting log types' do
198
+ expect do
199
+ logs.available_types
200
+ end.not_to raise_error
201
+ end
202
+
203
+ it 'does not error when getting logs' do
204
+ expect do
205
+ logs.get(:browser)
206
+ end.not_to raise_error
105
207
  end
106
208
  end
107
209
  end