capybara 3.35.0 → 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 (184) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +168 -5
  3. data/README.md +199 -39
  4. data/lib/capybara/config.rb +16 -4
  5. data/lib/capybara/driver/base.rb +4 -0
  6. data/lib/capybara/driver/node.rb +5 -1
  7. data/lib/capybara/dsl.rb +4 -10
  8. data/lib/capybara/helpers.rb +9 -14
  9. data/lib/capybara/minitest/spec.rb +18 -6
  10. data/lib/capybara/minitest.rb +14 -1
  11. data/lib/capybara/node/actions.rb +14 -9
  12. data/lib/capybara/node/base.rb +2 -1
  13. data/lib/capybara/node/document.rb +2 -2
  14. data/lib/capybara/node/element.rb +13 -2
  15. data/lib/capybara/node/finders.rb +11 -2
  16. data/lib/capybara/node/matchers.rb +25 -0
  17. data/lib/capybara/node/simple.rb +5 -1
  18. data/lib/capybara/node/whitespace_normalizer.rb +81 -0
  19. data/lib/capybara/queries/active_element_query.rb +18 -0
  20. data/lib/capybara/queries/ancestor_query.rb +2 -1
  21. data/lib/capybara/queries/base_query.rb +2 -2
  22. data/lib/capybara/queries/current_path_query.rb +1 -1
  23. data/lib/capybara/queries/selector_query.rb +40 -11
  24. data/lib/capybara/queries/sibling_query.rb +2 -1
  25. data/lib/capybara/queries/text_query.rb +1 -1
  26. data/lib/capybara/rack_test/browser.rb +64 -8
  27. data/lib/capybara/rack_test/driver.rb +4 -4
  28. data/lib/capybara/rack_test/form.rb +29 -7
  29. data/lib/capybara/rack_test/node.rb +32 -33
  30. data/lib/capybara/registration_container.rb +2 -5
  31. data/lib/capybara/registrations/drivers.rb +7 -7
  32. data/lib/capybara/registrations/servers.rb +37 -16
  33. data/lib/capybara/result.rb +2 -2
  34. data/lib/capybara/rspec/matcher_proxies.rb +6 -6
  35. data/lib/capybara/rspec/matchers/base.rb +8 -6
  36. data/lib/capybara/rspec/matchers/compound.rb +1 -1
  37. data/lib/capybara/rspec/matchers/have_selector.rb +9 -17
  38. data/lib/capybara/rspec/matchers.rb +21 -16
  39. data/lib/capybara/selector/builders/css_builder.rb +1 -1
  40. data/lib/capybara/selector/builders/xpath_builder.rb +1 -1
  41. data/lib/capybara/selector/css.rb +6 -6
  42. data/lib/capybara/selector/definition/button.rb +10 -5
  43. data/lib/capybara/selector/definition/checkbox.rb +1 -1
  44. data/lib/capybara/selector/definition/file_field.rb +1 -1
  45. data/lib/capybara/selector/definition/fillable_field.rb +1 -1
  46. data/lib/capybara/selector/definition/link.rb +2 -1
  47. data/lib/capybara/selector/definition/radio_button.rb +1 -1
  48. data/lib/capybara/selector/definition/table.rb +1 -1
  49. data/lib/capybara/selector/definition/table_row.rb +2 -2
  50. data/lib/capybara/selector/definition.rb +4 -2
  51. data/lib/capybara/selector/filter_set.rb +4 -7
  52. data/lib/capybara/selector/regexp_disassembler.rb +2 -5
  53. data/lib/capybara/selector/selector.rb +5 -1
  54. data/lib/capybara/selector.rb +252 -0
  55. data/lib/capybara/selenium/driver.rb +31 -54
  56. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +1 -1
  57. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +9 -5
  58. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +2 -7
  59. data/lib/capybara/selenium/extensions/html5_drag.rb +5 -4
  60. data/lib/capybara/selenium/node.rb +60 -38
  61. data/lib/capybara/selenium/nodes/chrome_node.rb +4 -16
  62. data/lib/capybara/selenium/nodes/edge_node.rb +19 -13
  63. data/lib/capybara/selenium/nodes/firefox_node.rb +3 -3
  64. data/lib/capybara/selenium/nodes/safari_node.rb +4 -4
  65. data/lib/capybara/selenium/patches/atoms.rb +1 -1
  66. data/lib/capybara/selenium/patches/pause_duration_fix.rb +1 -1
  67. data/lib/capybara/server/animation_disabler.rb +40 -23
  68. data/lib/capybara/server/middleware.rb +1 -1
  69. data/lib/capybara/server.rb +1 -1
  70. data/lib/capybara/session/config.rb +4 -2
  71. data/lib/capybara/session.rb +34 -34
  72. data/lib/capybara/spec/public/test.js +4 -0
  73. data/lib/capybara/spec/session/active_element_spec.rb +31 -0
  74. data/lib/capybara/spec/session/all_spec.rb +11 -15
  75. data/lib/capybara/spec/session/assert_text_spec.rb +17 -17
  76. data/lib/capybara/spec/session/attach_file_spec.rb +6 -0
  77. data/lib/capybara/spec/session/check_spec.rb +10 -0
  78. data/lib/capybara/spec/session/choose_spec.rb +6 -0
  79. data/lib/capybara/spec/session/click_link_or_button_spec.rb +9 -0
  80. data/lib/capybara/spec/session/click_link_spec.rb +12 -1
  81. data/lib/capybara/spec/session/current_scope_spec.rb +1 -1
  82. data/lib/capybara/spec/session/fill_in_spec.rb +6 -0
  83. data/lib/capybara/spec/session/find_link_spec.rb +10 -0
  84. data/lib/capybara/spec/session/find_spec.rb +15 -1
  85. data/lib/capybara/spec/session/first_spec.rb +1 -1
  86. data/lib/capybara/spec/session/frame/within_frame_spec.rb +2 -0
  87. data/lib/capybara/spec/session/has_all_selectors_spec.rb +5 -5
  88. data/lib/capybara/spec/session/has_ancestor_spec.rb +2 -2
  89. data/lib/capybara/spec/session/has_any_selectors_spec.rb +6 -2
  90. data/lib/capybara/spec/session/has_button_spec.rb +30 -0
  91. data/lib/capybara/spec/session/has_current_path_spec.rb +3 -3
  92. data/lib/capybara/spec/session/has_element_spec.rb +47 -0
  93. data/lib/capybara/spec/session/has_field_spec.rb +25 -1
  94. data/lib/capybara/spec/session/has_link_spec.rb +40 -0
  95. data/lib/capybara/spec/session/has_none_selectors_spec.rb +7 -7
  96. data/lib/capybara/spec/session/has_select_spec.rb +10 -4
  97. data/lib/capybara/spec/session/has_selector_spec.rb +15 -0
  98. data/lib/capybara/spec/session/has_table_spec.rb +13 -2
  99. data/lib/capybara/spec/session/has_text_spec.rb +6 -14
  100. data/lib/capybara/spec/session/matches_style_spec.rb +2 -0
  101. data/lib/capybara/spec/session/node_spec.rb +88 -1
  102. data/lib/capybara/spec/session/node_wrapper_spec.rb +1 -1
  103. data/lib/capybara/spec/session/reset_session_spec.rb +13 -0
  104. data/lib/capybara/spec/session/scroll_spec.rb +7 -5
  105. data/lib/capybara/spec/session/uncheck_spec.rb +1 -1
  106. data/lib/capybara/spec/session/visit_spec.rb +20 -0
  107. data/lib/capybara/spec/session/window/window_spec.rb +1 -1
  108. data/lib/capybara/spec/session/window/windows_spec.rb +1 -1
  109. data/lib/capybara/spec/session/within_spec.rb +13 -0
  110. data/lib/capybara/spec/spec_helper.rb +12 -5
  111. data/lib/capybara/spec/test_app.rb +91 -14
  112. data/lib/capybara/spec/views/animated.erb +1 -1
  113. data/lib/capybara/spec/views/form.erb +34 -4
  114. data/lib/capybara/spec/views/frame_child.erb +1 -1
  115. data/lib/capybara/spec/views/frame_one.erb +1 -1
  116. data/lib/capybara/spec/views/frame_parent.erb +1 -1
  117. data/lib/capybara/spec/views/frame_two.erb +1 -1
  118. data/lib/capybara/spec/views/initial_alert.erb +2 -1
  119. data/lib/capybara/spec/views/layout.erb +10 -0
  120. data/lib/capybara/spec/views/obscured.erb +1 -1
  121. data/lib/capybara/spec/views/offset.erb +2 -1
  122. data/lib/capybara/spec/views/path.erb +2 -2
  123. data/lib/capybara/spec/views/popup_one.erb +1 -1
  124. data/lib/capybara/spec/views/popup_two.erb +1 -1
  125. data/lib/capybara/spec/views/react.erb +2 -2
  126. data/lib/capybara/spec/views/scroll.erb +2 -1
  127. data/lib/capybara/spec/views/spatial.erb +1 -1
  128. data/lib/capybara/spec/views/with_animation.erb +2 -3
  129. data/lib/capybara/spec/views/with_base_tag.erb +2 -2
  130. data/lib/capybara/spec/views/with_dragula.erb +2 -2
  131. data/lib/capybara/spec/views/with_fixed_header_footer.erb +2 -1
  132. data/lib/capybara/spec/views/with_hover.erb +2 -2
  133. data/lib/capybara/spec/views/with_html.erb +5 -3
  134. data/lib/capybara/spec/views/with_jquery_animation.erb +1 -1
  135. data/lib/capybara/spec/views/with_js.erb +2 -3
  136. data/lib/capybara/spec/views/with_jstree.erb +1 -1
  137. data/lib/capybara/spec/views/with_namespace.erb +1 -0
  138. data/lib/capybara/spec/views/with_scope.erb +2 -2
  139. data/lib/capybara/spec/views/with_shadow.erb +31 -0
  140. data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
  141. data/lib/capybara/spec/views/with_sortable_js.erb +2 -2
  142. data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
  143. data/lib/capybara/spec/views/with_windows.erb +1 -1
  144. data/lib/capybara/spec/views/within_frames.erb +1 -1
  145. data/lib/capybara/version.rb +1 -1
  146. data/lib/capybara/window.rb +1 -1
  147. data/lib/capybara.rb +30 -30
  148. data/spec/basic_node_spec.rb +16 -3
  149. data/spec/capybara_spec.rb +12 -0
  150. data/spec/counter_spec.rb +35 -0
  151. data/spec/css_builder_spec.rb +1 -1
  152. data/spec/css_splitter_spec.rb +1 -1
  153. data/spec/dsl_spec.rb +5 -3
  154. data/spec/fixtures/selenium_driver_rspec_failure.rb +2 -2
  155. data/spec/fixtures/selenium_driver_rspec_success.rb +2 -2
  156. data/spec/minitest_spec.rb +12 -1
  157. data/spec/minitest_spec_spec.rb +4 -0
  158. data/spec/per_session_config_spec.rb +1 -1
  159. data/spec/rack_test_spec.rb +30 -12
  160. data/spec/result_spec.rb +41 -35
  161. data/spec/rspec/features_spec.rb +3 -3
  162. data/spec/rspec/scenarios_spec.rb +2 -2
  163. data/spec/rspec/shared_spec_matchers.rb +27 -3
  164. data/spec/rspec_matchers_spec.rb +25 -0
  165. data/spec/rspec_spec.rb +3 -3
  166. data/spec/sauce_spec_chrome.rb +5 -5
  167. data/spec/selector_spec.rb +4 -4
  168. data/spec/selenium_spec_chrome.rb +20 -18
  169. data/spec/selenium_spec_chrome_remote.rb +15 -19
  170. data/spec/selenium_spec_edge.rb +19 -6
  171. data/spec/selenium_spec_firefox.rb +26 -8
  172. data/spec/selenium_spec_firefox_remote.rb +18 -4
  173. data/spec/selenium_spec_ie.rb +7 -8
  174. data/spec/selenium_spec_safari.rb +34 -20
  175. data/spec/server_spec.rb +19 -7
  176. data/spec/shared_selenium_node.rb +0 -4
  177. data/spec/shared_selenium_session.rb +22 -14
  178. data/spec/spec_helper.rb +36 -3
  179. data/spec/whitespace_normalizer_spec.rb +54 -0
  180. data/spec/xpath_builder_spec.rb +1 -1
  181. metadata +49 -30
  182. data/lib/capybara/selenium/logger_suppressor.rb +0 -34
  183. data/lib/capybara/selenium/patches/action_pauser.rb +0 -26
  184. data/lib/capybara/spec/views/with_title.erb +0 -5
data/spec/result_spec.rb CHANGED
@@ -30,6 +30,15 @@ RSpec.describe Capybara::Result do
30
30
  result.last.text == 'Delta'
31
31
  end
32
32
 
33
+ it 'splats into multiple assignment' do
34
+ first, *rest, last = result
35
+
36
+ expect(first).to have_text 'Alpha'
37
+ expect(rest.first).to have_text 'Beta'
38
+ expect(rest.last).to have_text 'Gamma'
39
+ expect(last).to have_text 'Delta'
40
+ end
41
+
33
42
  it 'can supports values_at method' do
34
43
  expect(result.values_at(0, 2).map(&:text)).to eq(%w[Alpha Gamma])
35
44
  end
@@ -78,34 +87,31 @@ RSpec.describe Capybara::Result do
78
87
  expect(recalc_result[1...3].map(&:text)).to eq %w[Beta Gamma]
79
88
  expect(recalc_result[1..7].map(&:text)).to eq %w[Beta Gamma Delta]
80
89
  expect(recalc_result[2...-1].map(&:text)).to eq %w[Gamma]
81
- expect(recalc_result[2..-1].map(&:text)).to eq %w[Gamma Delta]
90
+ expect(recalc_result[2..-1].map(&:text)).to eq %w[Gamma Delta] # rubocop:disable Style/SlicingWithRange
91
+ expect(recalc_result[2..].map(&:text)).to eq %w[Gamma Delta]
82
92
  end
83
93
 
84
- eval <<~TEST, binding, __FILE__, __LINE__ + 1 if RUBY_VERSION.to_f > 2.5
85
- it 'supports endless ranges' do
86
- expect(result[2..].map(&:text)).to eq %w[Gamma Delta]
87
- end
88
- TEST
94
+ it 'supports endless ranges' do
95
+ expect(result[2..].map(&:text)).to eq %w[Gamma Delta]
96
+ end
89
97
 
90
- eval <<~TEST, binding, __FILE__, __LINE__ + 1 if RUBY_VERSION.to_f > 2.6
91
- it 'supports inclusive positive beginless ranges' do
92
- expect(result[..2].map(&:text)).to eq %w[Alpha Beta Gamma]
93
- end
98
+ it 'supports inclusive positive beginless ranges' do
99
+ expect(result[..2].map(&:text)).to eq %w[Alpha Beta Gamma]
100
+ end
94
101
 
95
- it 'supports inclusive negative beginless ranges' do
96
- expect(result[..-2].map(&:text)).to eq %w[Alpha Beta Gamma]
97
- expect(result[..-1].map(&:text)).to eq %w[Alpha Beta Gamma Delta]
98
- end
102
+ it 'supports inclusive negative beginless ranges' do
103
+ expect(result[..-2].map(&:text)).to eq %w[Alpha Beta Gamma]
104
+ expect(result[..-1].map(&:text)).to eq %w[Alpha Beta Gamma Delta]
105
+ end
99
106
 
100
- it 'supports exclusive positive beginless ranges' do
101
- expect(result[...2].map(&:text)).to eq %w[Alpha Beta]
102
- end
107
+ it 'supports exclusive positive beginless ranges' do
108
+ expect(result[...2].map(&:text)).to eq %w[Alpha Beta]
109
+ end
103
110
 
104
- it 'supports exclusive negative beginless ranges' do
105
- expect(result[...-2].map(&:text)).to eq %w[Alpha Beta]
106
- expect(result[...-1].map(&:text)).to eq %w[Alpha Beta Gamma]
107
- end
108
- TEST
111
+ it 'supports exclusive negative beginless ranges' do
112
+ expect(result[...-2].map(&:text)).to eq %w[Alpha Beta]
113
+ expect(result[...-1].map(&:text)).to eq %w[Alpha Beta Gamma]
114
+ end
109
115
 
110
116
  it 'works with filter blocks' do
111
117
  result = string.all('//li') { |node| node.text == 'Alpha' }
@@ -116,52 +122,52 @@ RSpec.describe Capybara::Result do
116
122
  it 'should evaluate filters lazily for idx' do
117
123
  skip 'JRuby has an issue with lazy enumerator evaluation' if jruby_lazy_enumerator_workaround?
118
124
  # Not processed until accessed
119
- expect(result.instance_variable_get('@result_cache').size).to be 0
125
+ expect(result.instance_variable_get(:@result_cache).size).to be 0
120
126
 
121
127
  # Only one retrieved when needed
122
128
  result.first
123
- expect(result.instance_variable_get('@result_cache').size).to be 1
129
+ expect(result.instance_variable_get(:@result_cache).size).to be 1
124
130
 
125
131
  # works for indexed access
126
132
  result[0]
127
- expect(result.instance_variable_get('@result_cache').size).to be 1
133
+ expect(result.instance_variable_get(:@result_cache).size).to be 1
128
134
 
129
135
  result[2]
130
- expect(result.instance_variable_get('@result_cache').size).to be 3
136
+ expect(result.instance_variable_get(:@result_cache).size).to be 3
131
137
 
132
138
  # All cached when converted to array
133
139
  result.to_a
134
- expect(result.instance_variable_get('@result_cache').size).to eq 4
140
+ expect(result.instance_variable_get(:@result_cache).size).to eq 4
135
141
  end
136
142
 
137
143
  it 'should evaluate filters lazily for range' do
138
144
  skip 'JRuby has an issue with lazy enumerator evaluation' if jruby_lazy_enumerator_workaround?
139
145
  result[0..1]
140
- expect(result.instance_variable_get('@result_cache').size).to be 2
146
+ expect(result.instance_variable_get(:@result_cache).size).to be 2
141
147
 
142
148
  expect(result[0..7].size).to eq 4
143
- expect(result.instance_variable_get('@result_cache').size).to be 4
149
+ expect(result.instance_variable_get(:@result_cache).size).to be 4
144
150
  end
145
151
 
146
152
  it 'should evaluate filters lazily for idx and length' do
147
153
  skip 'JRuby has an issue with lazy enumerator evaluation' if jruby_lazy_enumerator_workaround?
148
154
  result[1, 2]
149
- expect(result.instance_variable_get('@result_cache').size).to be 3
155
+ expect(result.instance_variable_get(:@result_cache).size).to be 3
150
156
 
151
157
  expect(result[2, 5].size).to eq 2
152
- expect(result.instance_variable_get('@result_cache').size).to be 4
158
+ expect(result.instance_variable_get(:@result_cache).size).to be 4
153
159
  end
154
160
 
155
161
  it 'should only need to evaluate one result for any?' do
156
162
  skip 'JRuby has an issue with lazy enumerator evaluation' if jruby_lazy_enumerator_workaround?
157
163
  result.any?
158
- expect(result.instance_variable_get('@result_cache').size).to be 1
164
+ expect(result.instance_variable_get(:@result_cache).size).to be 1
159
165
  end
160
166
 
161
167
  it 'should evaluate all elements when #to_a called' do
162
168
  # All cached when converted to array
163
169
  result.to_a
164
- expect(result.instance_variable_get('@result_cache').size).to eq 4
170
+ expect(result.instance_variable_get(:@result_cache).size).to eq 4
165
171
  end
166
172
 
167
173
  describe '#each' do
@@ -170,7 +176,7 @@ RSpec.describe Capybara::Result do
170
176
  results = []
171
177
  result.each do |el|
172
178
  results << el
173
- expect(result.instance_variable_get('@result_cache').size).to eq results.size
179
+ expect(result.instance_variable_get(:@result_cache).size).to eq results.size
174
180
  end
175
181
 
176
182
  expect(results.size).to eq 4
@@ -184,7 +190,7 @@ RSpec.describe Capybara::Result do
184
190
  it 'lazily evaluates' do
185
191
  skip 'JRuby has an issue with lazy enumerator evaluation' if jruby_lazy_enumerator_workaround?
186
192
  result.each.with_index do |_el, idx|
187
- expect(result.instance_variable_get('@result_cache').size).to eq(idx + 1) # 0 indexing
193
+ expect(result.instance_variable_get(:@result_cache).size).to eq(idx + 1) # 0 indexing
188
194
  end
189
195
  end
190
196
  end
@@ -91,11 +91,11 @@ end
91
91
 
92
92
  ffeature 'if ffeature aliases focused tag then' do # rubocop:disable RSpec/Focus
93
93
  scenario 'scenario inside this feature has metatag focus tag' do |example|
94
- expect(example.metadata[:focus]).to eq true
94
+ expect(example.metadata[:focus]).to be true
95
95
  end
96
96
 
97
- scenario 'other scenarios also has metatag focus tag ' do |example|
98
- expect(example.metadata[:focus]).to eq true
97
+ scenario 'other scenarios also has metatag focus tag' do |example|
98
+ expect(example.metadata[:focus]).to be true
99
99
  end
100
100
  end
101
101
  # rubocop:enable RSpec/RepeatedExample, RSpec/MultipleDescribes
@@ -11,12 +11,12 @@ end
11
11
 
12
12
  feature 'if fscenario aliases focused tag then' do
13
13
  fscenario 'scenario should have focused meta tag' do |example| # rubocop:disable RSpec/Focus
14
- expect(example.metadata[:focus]).to eq true
14
+ expect(example.metadata[:focus]).to be true
15
15
  end
16
16
  end
17
17
 
18
18
  feature 'if xscenario aliases to pending then' do
19
- xscenario "this test should be 'temporarily disabled with xscenario'" do
19
+ xscenario "this test should be 'temporarily disabled with xscenario'" do # rubocop:disable RSpec/PendingWithoutReason
20
20
  end
21
21
  end
22
22
 
@@ -120,7 +120,7 @@ RSpec.shared_examples Capybara::RSpecMatchers do |session, _mode|
120
120
 
121
121
  describe 'have_xpath matcher' do
122
122
  it 'gives proper description' do
123
- expect(have_xpath('//h1').description).to eq("have visible xpath \"\/\/h1\"")
123
+ expect(have_xpath('//h1').description).to eq('have visible xpath "//h1"')
124
124
  end
125
125
 
126
126
  context 'on a string' do
@@ -503,6 +503,30 @@ RSpec.shared_examples Capybara::RSpecMatchers do |session, _mode|
503
503
  end
504
504
  end
505
505
 
506
+ describe 'have_element matcher' do
507
+ let(:html) { '<img src="/img.jpg" alt="a JPEG"><img src="/img.png" alt="a PNG">' }
508
+
509
+ it 'gives proper description' do
510
+ expect(have_element('img').description).to eq('have visible element "img"')
511
+ end
512
+
513
+ it 'passes if there is such a element' do
514
+ expect(html).to have_element('img', src: '/img.jpg')
515
+ end
516
+
517
+ it 'fails if there is no such element' do
518
+ expect do
519
+ expect(html).to have_element('photo')
520
+ end.to raise_error(/expected to find element "photo"/)
521
+ end
522
+
523
+ it 'supports compounding' do
524
+ expect(html).to have_element('img', alt: 'a JPEG').and have_element('img', src: '/img.png')
525
+ expect(html).to have_element('photo').or have_element('img', src: '/img.jpg')
526
+ expect(html).to have_no_element('photo').and have_element('img', alt: 'a PNG')
527
+ end
528
+ end
529
+
506
530
  describe 'have_link matcher' do
507
531
  let(:html) { '<a href="#">Just a link</a><a href="#">Another link</a>' }
508
532
 
@@ -840,10 +864,10 @@ RSpec.shared_examples Capybara::RSpecMatchers do |session, _mode|
840
864
  end
841
865
 
842
866
  it 'gives proper description when :visible option passed' do
843
- expect(have_table('Lovely table', visible: true).description).to eq('have visible table "Lovely table"')
867
+ expect(have_table('Lovely table', visible: true).description).to eq('have visible table "Lovely table"') # rubocop:disable Capybara/VisibilityMatcher
844
868
  expect(have_table('Lovely table', visible: :hidden).description).to eq('have non-visible table "Lovely table"')
845
869
  expect(have_table('Lovely table', visible: :all).description).to eq('have table "Lovely table"')
846
- expect(have_table('Lovely table', visible: false).description).to eq('have table "Lovely table"')
870
+ expect(have_table('Lovely table', visible: false).description).to eq('have table "Lovely table"') # rubocop:disable Capybara/VisibilityMatcher
847
871
  end
848
872
 
849
873
  it 'passes if there is such a table' do
@@ -56,6 +56,31 @@ RSpec.describe 'Capybara RSpec Matchers', type: :feature do
56
56
  end
57
57
  end
58
58
 
59
+ context 'with a filter block' do
60
+ it 'applies the filter' do
61
+ visit('/with_html')
62
+ expect(page).to have_selector(:css, 'input#test_field')
63
+
64
+ expect(page).to have_selector(:css, 'input', count: 1) { |input| input.value == 'monkey' }
65
+ expect(page).to have_selector(:css, 'input', count: 1) do |input|
66
+ input.value == 'monkey'
67
+ end
68
+ expect(page).to have_no_selector(:css, 'input#test_field') { |input| input.value == 'not a monkey' }
69
+ expect(page).to have_no_selector(:css, 'input#test_field') do |input|
70
+ input.value == 'not a monkey'
71
+ end
72
+
73
+ expect(page).not_to have_selector(:css, 'input#test_field') { |input| input.value == 'not a monkey' }
74
+ expect(page).not_to have_selector(:css, 'input#test_field') do |input|
75
+ input.value == 'not a monkey'
76
+ end
77
+ expect(page).not_to have_no_selector(:css, 'input', count: 1) { |input| input.value == 'monkey' }
78
+ expect(page).not_to have_no_selector(:css, 'input#test_field', count: 1) do |input|
79
+ input.value == 'monkey'
80
+ end
81
+ end
82
+ end
83
+
59
84
  def error_msg_for(&block)
60
85
  expect(&block).to(raise_error { |e| return e.message })
61
86
  end
data/spec/rspec_spec.rb CHANGED
@@ -15,7 +15,7 @@ RSpec.describe 'capybara/rspec' do
15
15
  expect(self.class.ancestors).to include Capybara::RSpecMatcherProxies
16
16
  end
17
17
 
18
- context 'resetting session' do
18
+ context 'resetting session', order: :defined do
19
19
  it 'sets a cookie in one example...' do
20
20
  visit('/set_cookie')
21
21
  expect(page.body).to include('Cookie set to test_cookie')
@@ -27,7 +27,7 @@ RSpec.describe 'capybara/rspec' do
27
27
  end
28
28
  end
29
29
 
30
- context 'setting the current driver' do
30
+ context 'setting the current driver', order: :defined do
31
31
  it 'sets the current driver in one example...' do
32
32
  Capybara.current_driver = :selenium
33
33
  end
@@ -37,7 +37,7 @@ RSpec.describe 'capybara/rspec' do
37
37
  end
38
38
  end
39
39
 
40
- 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
41
41
  expect(Capybara.current_driver).to eq(Capybara.javascript_driver)
42
42
  end
43
43
 
@@ -10,14 +10,14 @@ require 'sauce_whisk'
10
10
 
11
11
  Capybara.register_driver :sauce_chrome do |app|
12
12
  options = {
13
- selenium_version: '3.141.59',
13
+ selenium_version: '4.8.0',
14
14
  platform: 'macOS 10.12',
15
15
  browser_name: 'chrome',
16
16
  version: '65.0',
17
17
  name: 'Capybara test',
18
- build: ENV['TRAVIS_REPO_SLUG'] || "Ruby-RSpec-Selenium: Local-#{Time.now.to_i}",
19
- username: ENV['SAUCE_USERNAME'],
20
- access_key: ENV['SAUCE_ACCESS_KEY']
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
21
  }
22
22
 
23
23
  options.delete(:browser_name)
@@ -27,7 +27,7 @@ Capybara.register_driver :sauce_chrome do |app|
27
27
 
28
28
  Capybara::Selenium::Driver.new(app,
29
29
  browser: :remote, url: url,
30
- desired_capabilities: capabilities,
30
+ capabilities: capabilities,
31
31
  options: Selenium::WebDriver::Chrome::Options.new(args: ['']))
32
32
  end
33
33
 
@@ -111,13 +111,13 @@ RSpec.describe Capybara do
111
111
  end
112
112
 
113
113
  describe 'adding a selector' do
114
- it 'can set default visiblity' do
114
+ it 'can set default visibility' do
115
115
  described_class.add_selector :hidden_field do
116
116
  visible :hidden
117
117
  css { |_sel| 'input[type="hidden"]' }
118
118
  end
119
119
 
120
- expect(string).to have_no_css('input[type="hidden"]')
120
+ expect(string).to have_no_css('input[type="hidden"]') # rubocop:disable Capybara/SpecificMatcher
121
121
  expect(string).to have_selector(:hidden_field)
122
122
  end
123
123
  end
@@ -159,7 +159,7 @@ RSpec.describe Capybara do
159
159
  end
160
160
 
161
161
  it 'returns nil if no match' do
162
- expect(Capybara::Selector.for('nothing')).to be nil
162
+ expect(Capybara::Selector.for('nothing')).to be_nil
163
163
  end
164
164
  end
165
165
 
@@ -471,7 +471,7 @@ RSpec.describe Capybara do
471
471
  end
472
472
 
473
473
  it 'includes wildcarded keys in description' do
474
- 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) }
475
475
  .to(raise_error do |e|
476
476
  expect(e).to be_a(Capybara::ElementNotFound)
477
477
  expect(e.message).to include 'not_there => bad'
@@ -8,11 +8,16 @@ require 'rspec/shared_spec_matchers'
8
8
 
9
9
  CHROME_DRIVER = :selenium_chrome
10
10
 
11
- Selenium::WebDriver::Chrome.path = '/usr/bin/google-chrome-beta' if ENV['CI'] && ENV['CHROME_BETA']
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
12
20
 
13
- browser_options = ::Selenium::WebDriver::Chrome::Options.new
14
- browser_options.headless! if ENV['HEADLESS']
15
- browser_options.add_option(:w3c, ENV['W3C'] != 'false')
16
21
  # Chromedriver 77 requires setting this for headless mode on linux
17
22
  # Different versions of Chrome/selenium-webdriver require setting differently - jus set them all
18
23
  browser_options.add_preference('download.default_directory', Capybara.save_path)
@@ -83,12 +88,10 @@ Capybara::SpecHelper.run_specs TestSessions::Chrome, CHROME_DRIVER.to_s, capybar
83
88
  case example.metadata[:full_description]
84
89
  when /#click_link can download a file$/
85
90
  skip 'Need to figure out testing of file downloading on windows platform' if Gem.win_platform?
86
- when /Capybara::Session selenium_chrome Capybara::Window#maximize/
87
- pending "Chrome headless doesn't support maximize" if ENV['HEADLESS']
88
- when /Capybara::Window#fullscreen should be able to fullscreen the window/
89
- skip 'Chromedriver hangs on attempts to fullscreen in headless mode' if ENV['HEADLESS']
90
- when /node #right_click delay should delay the mouse up/
91
- skip "Legacy selenium doesn't support separate right button down/up" if ENV['W3C'] == 'false'
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)
92
95
  end
93
96
  end
94
97
 
@@ -185,23 +188,22 @@ RSpec.describe 'Capybara::Session with chrome' do
185
188
  end
186
189
 
187
190
  describe 'log access' do
188
- before { skip 'Only makes sense in W3C mode' if ENV['W3C'] == 'false' }
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
189
196
 
190
197
  it 'does not error getting log types' do
191
- skip if Gem::Requirement.new('< 75.0.3770.90').satisfied_by? chromedriver_version
192
198
  expect do
193
- session.driver.browser.manage.logs.available_types
199
+ logs.available_types
194
200
  end.not_to raise_error
195
201
  end
196
202
 
197
203
  it 'does not error when getting logs' do
198
204
  expect do
199
- session.driver.browser.manage.logs.get(:browser)
205
+ logs.get(:browser)
200
206
  end.not_to raise_error
201
207
  end
202
208
  end
203
-
204
- def chromedriver_version
205
- Gem::Version.new(session.driver.browser.capabilities['chrome']['chromedriverVersion'].split[0])
206
- end
207
209
  end
@@ -21,8 +21,8 @@ def ensure_selenium_running!
21
21
  rescue StandardError
22
22
  if timer.expired?
23
23
  raise 'Selenium is not running. ' \
24
- "You can run a selenium server easily with: \n" \
25
- ' $ docker-compose up -d selenium_chrome'
24
+ "You can run a selenium server easily with: \n " \
25
+ '$ docker-compose up -d selenium_chrome'
26
26
  else
27
27
  puts 'Waiting for Selenium docker instance...'
28
28
  sleep 1
@@ -32,18 +32,17 @@ def ensure_selenium_running!
32
32
  end
33
33
 
34
34
  def selenium_gte?(version)
35
- defined?(Selenium::WebDriver::VERSION) && (Selenium::WebDriver::VERSION.to_f >= version)
35
+ defined?(Selenium::WebDriver::VERSION) && (Gem::Version.new(Selenium::WebDriver::VERSION) >= Gem::Version.new(version))
36
36
  end
37
37
 
38
38
  Capybara.register_driver :selenium_chrome_remote do |app|
39
39
  ensure_selenium_running!
40
40
 
41
41
  url = "http://#{selenium_host}:#{selenium_port}/wd/hub"
42
- browser_options = ::Selenium::WebDriver::Chrome::Options.new
42
+ browser_options = Selenium::WebDriver::Chrome::Options.new
43
43
 
44
44
  Capybara::Selenium::Driver.new app,
45
45
  browser: :remote,
46
- desired_capabilities: :chrome,
47
46
  options: browser_options,
48
47
  url: url
49
48
  end
@@ -58,12 +57,10 @@ skipped_tests = %i[response_headers status_code trigger download]
58
57
 
59
58
  Capybara::SpecHelper.run_specs TestSessions::Chrome, CHROME_REMOTE_DRIVER.to_s, capybara_skip: skipped_tests do |example|
60
59
  case example.metadata[:full_description]
61
- when 'Capybara::Session selenium_chrome_remote #attach_file with multipart form should not break when using HTML5 multiple file input uploading multiple files',
62
- 'Capybara::Session selenium_chrome_remote #attach_file with multipart form should fire change once for each set of files uploaded',
63
- 'Capybara::Session selenium_chrome_remote #attach_file with multipart form should fire change once when uploading multiple files from empty'
64
- pending "Selenium with Remote Chrome doesn't support multiple file upload" unless selenium_gte?(3.14)
65
- when /node #right_click delay should delay the mouse up/
66
- skip "Legacy selenium doesn't support separate right button down/up" if ENV['W3C'] == 'false'
60
+ when /Capybara::Session selenium_chrome_remote node #shadow_root should get visible text/
61
+ pending "Selenium doesn't currently support getting visible text for shadow root elements"
62
+ when /Capybara::Session selenium_chrome_remote node #shadow_root/
63
+ skip 'Not supported with this chromedriver version' if chromedriver_lt?('96.0', @session)
67
64
  end
68
65
  end
69
66
 
@@ -78,23 +75,22 @@ RSpec.describe 'Capybara::Session with remote Chrome' do
78
75
  end
79
76
 
80
77
  describe 'log access' do
81
- before { skip 'Only makes sense in W3C mode' if ENV['W3C'] == 'false' }
78
+ let(:logs) do
79
+ session.driver.browser.then do |chrome_driver|
80
+ chrome_driver.respond_to?(:logs) ? chrome_driver : chrome_driver.manage
81
+ end.logs
82
+ end
82
83
 
83
84
  it 'does not error when getting log types' do
84
- skip unless Gem::Requirement.new('>= 75.0.3770.90').satisfied_by? chromedriver_version
85
85
  expect do
86
- session.driver.browser.manage.logs.available_types
86
+ logs.available_types
87
87
  end.not_to raise_error
88
88
  end
89
89
 
90
90
  it 'does not error when getting logs' do
91
91
  expect do
92
- session.driver.browser.manage.logs.get(:browser)
92
+ logs.get(:browser)
93
93
  end.not_to raise_error
94
94
  end
95
95
  end
96
-
97
- def chromedriver_version
98
- Gem::Version.new(session.driver.browser.capabilities['chrome']['chromedriverVersion'].split[0])
99
- end
100
96
  end
@@ -10,15 +10,26 @@ require 'rspec/shared_spec_matchers'
10
10
  # Selenium::WebDriver::Edge::Service.driver_path = '/usr/local/bin/msedgedriver'
11
11
  # end
12
12
 
13
- if ::Selenium::WebDriver::Platform.mac?
14
- Selenium::WebDriver::EdgeChrome.path = '/Applications/Microsoft Edge Dev.app/Contents/MacOS/Microsoft Edge Dev'
13
+ Selenium::WebDriver.logger.ignore(:selenium_manager)
14
+
15
+ if Selenium::WebDriver::Platform.mac?
16
+ Selenium::WebDriver::Edge.path = '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge'
15
17
  end
16
18
 
17
19
  Capybara.register_driver :selenium_edge do |app|
18
20
  # ::Selenium::WebDriver.logger.level = "debug"
19
21
  # If we don't create an options object the path set above won't be used
20
- browser_options = ::Selenium::WebDriver::EdgeChrome::Options.new
21
- Capybara::Selenium::Driver.new(app, browser: :edge_chrome, options: browser_options).tap do |driver|
22
+
23
+ # browser_options = Selenium::WebDriver::Edge::Options.new
24
+ # browser_options.add_argument('--headless') if ENV['HEADLESS']
25
+
26
+ browser_options = if ENV['HEADLESS']
27
+ Selenium::WebDriver::Options.edge(args: ['--headless=new'])
28
+ else
29
+ Selenium::WebDriver::Options.edge
30
+ end
31
+
32
+ Capybara::Selenium::Driver.new(app, browser: :edge, options: browser_options).tap do |driver|
22
33
  driver.browser
23
34
  driver.download_path = Capybara.save_path
24
35
  end
@@ -30,12 +41,14 @@ end
30
41
 
31
42
  skipped_tests = %i[response_headers status_code trigger]
32
43
 
33
- Capybara::SpecHelper.log_selenium_driver_version(Selenium::WebDriver::EdgeChrome) if ENV['CI']
44
+ Capybara::SpecHelper.log_selenium_driver_version(Selenium::WebDriver::Edge) if ENV['CI']
34
45
 
35
46
  Capybara::SpecHelper.run_specs TestSessions::SeleniumEdge, 'selenium', capybara_skip: skipped_tests do |example|
36
47
  case example.metadata[:full_description]
37
48
  when 'Capybara::Session selenium #attach_file with a block can upload by clicking the file input'
38
- pending "EdgeChrome doesn't allow clicking on file inputs"
49
+ pending "Edge doesn't allow clicking on file inputs"
50
+ when /Capybara::Session selenium node #shadow_root should get visible text/
51
+ pending "Selenium doesn't currently support getting visible text for shadow root elements"
39
52
  end
40
53
  end
41
54
 
@@ -6,8 +6,10 @@ require 'shared_selenium_session'
6
6
  require 'shared_selenium_node'
7
7
  require 'rspec/shared_spec_matchers'
8
8
 
9
- browser_options = ::Selenium::WebDriver::Firefox::Options.new
10
- browser_options.headless! if ENV['HEADLESS']
9
+ Selenium::WebDriver.logger.ignore(:selenium_manager)
10
+
11
+ browser_options = Selenium::WebDriver::Firefox::Options.new
12
+ browser_options.add_argument '-headless' if ENV['HEADLESS']
11
13
  # browser_options.add_option("log", {"level": "trace"})
12
14
 
13
15
  browser_options.profile = Selenium::WebDriver::Firefox::Profile.new.tap do |profile|
@@ -15,6 +17,7 @@ browser_options.profile = Selenium::WebDriver::Firefox::Profile.new.tap do |prof
15
17
  profile['browser.download.folderList'] = 2
16
18
  profile['browser.helperApps.neverAsk.saveToDisk'] = 'text/csv'
17
19
  profile['browser.startup.homepage'] = 'about:blank' # workaround bug in Selenium 4 alpha4-7
20
+ profile['accessibility.tabfocus'] = 7 # make tab move over links too
18
21
  end
19
22
 
20
23
  Capybara.register_driver :selenium_firefox do |app|
@@ -59,18 +62,33 @@ Capybara::SpecHelper.run_specs TestSessions::SeleniumFirefox, 'selenium', capyba
59
62
  when 'Capybara::Session selenium #attach_file with multipart form should fire change once when uploading multiple files from empty'
60
63
  pending "FF < 62 doesn't support setting all files at once" if firefox_lt?(62, @session)
61
64
  when 'Capybara::Session selenium #accept_confirm should work with nested modals'
62
- skip 'Broken in 63 <= FF < 69 - https://bugzilla.mozilla.org/show_bug.cgi?id=1487358' if firefox_gte?(63, @session) && firefox_lt?(69, @session)
63
- skip 'Hangs in 69 <= FF < 71 - Dont know what issue for this - previous issue was closed as fixed but it is not' if firefox_gte?(69, @session) && firefox_lt?(71, @session)
64
- skip 'Broken again intermittently in FF 71 - jus skip it'
65
+ skip 'Broken in 63 <= FF < 69 - https://bugzilla.mozilla.org/show_bug.cgi?id=1487358' if firefox_gte?(63, @session) && firefox_lt?(69, @session)
66
+ skip 'Hangs in 69 <= FF < 71 - Dont know what issue for this - previous issue was closed as fixed but it is not' if firefox_gte?(69, @session) && firefox_lt?(71, @session)
67
+ skip 'Broken again intermittently in FF 71 - jus skip it' if firefox_lt?(109, @session) # don't really know when it was fixed
65
68
  when 'Capybara::Session selenium #click_link can download a file'
66
69
  skip 'Need to figure out testing of file downloading on windows platform' if Gem.win_platform?
67
70
  when 'Capybara::Session selenium #reset_session! removes ALL cookies'
68
71
  pending "Geckodriver doesn't provide a way to remove cookies outside the current domain"
69
72
  when /drag_to.*HTML5/
70
- pending "Firefox < 62 doesn't support a DataTransfer constuctor" if firefox_lt?(62.0, @session)
73
+ pending "Firefox < 62 doesn't support a DataTransfer constructor" if firefox_lt?(62.0, @session)
71
74
  when 'Capybara::Session selenium #accept_alert should handle the alert if the page changes',
72
75
  'Capybara::Session selenium #accept_alert with an asynchronous alert should accept the alert'
73
- skip 'No clue what Firefox is doing here - works fine on MacOS locally'
76
+ skip 'No clue what Firefox is doing here - works fine on MacOS locally' if firefox_lt?(109, @session) # don't really know when it was fixed
77
+ when 'Capybara::Session selenium node #shadow_root should find elements inside the shadow dom using CSS',
78
+ 'Capybara::Session selenium node #shadow_root should find nested shadow roots',
79
+ 'Capybara::Session selenium node #shadow_root should click on elements',
80
+ 'Capybara::Session selenium node #shadow_root should use convenience methods once moved to a descendant of the shadow root',
81
+ 'Capybara::Session selenium node #shadow_root should produce error messages when failing',
82
+ 'Capybara::Session with firefox with selenium driver #evaluate_script returns a shadow root'
83
+ pending "Firefox doesn't yet have full W3C shadow root support" if firefox_lt?(113, @session)
84
+ when 'Capybara::Session selenium #fill_in should handle carriage returns with line feeds in a textarea correctly'
85
+ pending 'Not sure what firefox is doing here'
86
+ when /Capybara::Session selenium node #shadow_root should get visible text/
87
+ pending "Selenium doesn't currently support getting visible text for shadow root elements"
88
+ when /Capybara::Session selenium node #shadow_root/
89
+ skip 'Not supported with this geckodriver version' if geckodriver_lt?('0.31.0', @session)
90
+ when /Capybara::Session selenium node #set should submit single text input forms if ended with \\n/
91
+ pending 'Firefox/geckodriver doesn\'t submit with values ending in \n'
74
92
  end
75
93
  end
76
94
 
@@ -102,7 +120,7 @@ RSpec.describe 'Capybara::Session with firefox' do # rubocop:disable RSpec/Multi
102
120
  end
103
121
 
104
122
  it 'should fill in a datetime input with a String' do
105
- # FF doesn't currently support datetime-local so this is really just a text input
123
+ pending 'Need to figure out what string format this will actually accept'
106
124
  session.fill_in('form_datetime', with: datetime.iso8601)
107
125
  session.click_button('awesome')
108
126
  expect(Time.parse(extract_results(session)['datetime'])).to eq datetime