capybara 3.23.0 → 3.35.3

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 (183) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +264 -11
  3. data/README.md +10 -6
  4. data/lib/capybara.rb +20 -8
  5. data/lib/capybara/config.rb +10 -8
  6. data/lib/capybara/cucumber.rb +1 -1
  7. data/lib/capybara/driver/base.rb +4 -0
  8. data/lib/capybara/driver/node.rb +4 -0
  9. data/lib/capybara/dsl.rb +10 -2
  10. data/lib/capybara/helpers.rb +28 -2
  11. data/lib/capybara/minitest.rb +232 -144
  12. data/lib/capybara/minitest/spec.rb +156 -97
  13. data/lib/capybara/node/actions.rb +36 -36
  14. data/lib/capybara/node/base.rb +6 -6
  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 +77 -33
  18. data/lib/capybara/node/finders.rb +24 -17
  19. data/lib/capybara/node/matchers.rb +79 -64
  20. data/lib/capybara/node/simple.rb +11 -4
  21. data/lib/capybara/queries/ancestor_query.rb +6 -10
  22. data/lib/capybara/queries/base_query.rb +2 -1
  23. data/lib/capybara/queries/current_path_query.rb +14 -4
  24. data/lib/capybara/queries/selector_query.rb +259 -23
  25. data/lib/capybara/queries/sibling_query.rb +5 -11
  26. data/lib/capybara/queries/style_query.rb +1 -1
  27. data/lib/capybara/queries/text_query.rb +13 -1
  28. data/lib/capybara/rack_test/browser.rb +13 -4
  29. data/lib/capybara/rack_test/driver.rb +2 -1
  30. data/lib/capybara/rack_test/form.rb +2 -2
  31. data/lib/capybara/rack_test/node.rb +42 -6
  32. data/lib/capybara/registration_container.rb +44 -0
  33. data/lib/capybara/registrations/drivers.rb +18 -12
  34. data/lib/capybara/registrations/patches/puma_ssl.rb +29 -0
  35. data/lib/capybara/registrations/servers.rb +9 -2
  36. data/lib/capybara/result.rb +39 -19
  37. data/lib/capybara/rspec.rb +2 -0
  38. data/lib/capybara/rspec/matcher_proxies.rb +5 -5
  39. data/lib/capybara/rspec/matchers.rb +97 -74
  40. data/lib/capybara/rspec/matchers/base.rb +19 -6
  41. data/lib/capybara/rspec/matchers/count_sugar.rb +2 -1
  42. data/lib/capybara/rspec/matchers/have_ancestor.rb +5 -7
  43. data/lib/capybara/rspec/matchers/have_current_path.rb +2 -2
  44. data/lib/capybara/rspec/matchers/have_selector.rb +15 -10
  45. data/lib/capybara/rspec/matchers/have_sibling.rb +4 -7
  46. data/lib/capybara/rspec/matchers/have_text.rb +4 -7
  47. data/lib/capybara/rspec/matchers/have_title.rb +2 -2
  48. data/lib/capybara/rspec/matchers/match_selector.rb +3 -3
  49. data/lib/capybara/rspec/matchers/match_style.rb +7 -2
  50. data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
  51. data/lib/capybara/selector.rb +46 -19
  52. data/lib/capybara/selector/builders/css_builder.rb +10 -6
  53. data/lib/capybara/selector/builders/xpath_builder.rb +4 -2
  54. data/lib/capybara/selector/css.rb +1 -1
  55. data/lib/capybara/selector/definition.rb +13 -11
  56. data/lib/capybara/selector/definition/button.rb +32 -15
  57. data/lib/capybara/selector/definition/checkbox.rb +2 -2
  58. data/lib/capybara/selector/definition/css.rb +3 -1
  59. data/lib/capybara/selector/definition/datalist_input.rb +2 -2
  60. data/lib/capybara/selector/definition/datalist_option.rb +1 -1
  61. data/lib/capybara/selector/definition/element.rb +3 -2
  62. data/lib/capybara/selector/definition/field.rb +1 -1
  63. data/lib/capybara/selector/definition/file_field.rb +1 -1
  64. data/lib/capybara/selector/definition/fillable_field.rb +2 -2
  65. data/lib/capybara/selector/definition/label.rb +5 -3
  66. data/lib/capybara/selector/definition/link.rb +8 -0
  67. data/lib/capybara/selector/definition/option.rb +1 -1
  68. data/lib/capybara/selector/definition/radio_button.rb +2 -2
  69. data/lib/capybara/selector/definition/select.rb +33 -14
  70. data/lib/capybara/selector/definition/table.rb +6 -3
  71. data/lib/capybara/selector/definition/table_row.rb +2 -2
  72. data/lib/capybara/selector/filter_set.rb +13 -11
  73. data/lib/capybara/selector/filters/base.rb +6 -1
  74. data/lib/capybara/selector/filters/locator_filter.rb +1 -1
  75. data/lib/capybara/selector/regexp_disassembler.rb +7 -0
  76. data/lib/capybara/selector/selector.rb +13 -3
  77. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -1
  78. data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -1
  79. data/lib/capybara/selenium/atoms/src/getAttribute.js +1 -1
  80. data/lib/capybara/selenium/atoms/src/isDisplayed.js +10 -10
  81. data/lib/capybara/selenium/driver.rb +86 -24
  82. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +24 -21
  83. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +21 -19
  84. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +17 -1
  85. data/lib/capybara/selenium/driver_specializations/safari_driver.rb +0 -4
  86. data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
  87. data/lib/capybara/selenium/extensions/find.rb +37 -26
  88. data/lib/capybara/selenium/extensions/html5_drag.rb +55 -11
  89. data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
  90. data/lib/capybara/selenium/extensions/scroll.rb +8 -10
  91. data/lib/capybara/selenium/logger_suppressor.rb +8 -2
  92. data/lib/capybara/selenium/node.rb +160 -40
  93. data/lib/capybara/selenium/nodes/chrome_node.rb +72 -12
  94. data/lib/capybara/selenium/nodes/edge_node.rb +32 -14
  95. data/lib/capybara/selenium/nodes/firefox_node.rb +28 -32
  96. data/lib/capybara/selenium/nodes/safari_node.rb +5 -29
  97. data/lib/capybara/selenium/patches/action_pauser.rb +26 -0
  98. data/lib/capybara/selenium/patches/atoms.rb +4 -4
  99. data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
  100. data/lib/capybara/selenium/patches/logs.rb +32 -7
  101. data/lib/capybara/server.rb +19 -3
  102. data/lib/capybara/server/animation_disabler.rb +8 -3
  103. data/lib/capybara/server/checker.rb +1 -1
  104. data/lib/capybara/server/middleware.rb +22 -10
  105. data/lib/capybara/session.rb +66 -40
  106. data/lib/capybara/session/config.rb +11 -3
  107. data/lib/capybara/session/matchers.rb +11 -11
  108. data/lib/capybara/spec/public/offset.js +6 -0
  109. data/lib/capybara/spec/public/test.js +75 -7
  110. data/lib/capybara/spec/session/accept_alert_spec.rb +1 -1
  111. data/lib/capybara/spec/session/all_spec.rb +60 -5
  112. data/lib/capybara/spec/session/ancestor_spec.rb +5 -0
  113. data/lib/capybara/spec/session/assert_text_spec.rb +9 -5
  114. data/lib/capybara/spec/session/check_spec.rb +6 -0
  115. data/lib/capybara/spec/session/click_button_spec.rb +16 -0
  116. data/lib/capybara/spec/session/click_link_or_button_spec.rb +9 -0
  117. data/lib/capybara/spec/session/current_url_spec.rb +11 -1
  118. data/lib/capybara/spec/session/fill_in_spec.rb +29 -0
  119. data/lib/capybara/spec/session/find_spec.rb +55 -0
  120. data/lib/capybara/spec/session/has_ancestor_spec.rb +2 -0
  121. data/lib/capybara/spec/session/has_button_spec.rb +51 -0
  122. data/lib/capybara/spec/session/has_css_spec.rb +26 -4
  123. data/lib/capybara/spec/session/has_current_path_spec.rb +15 -2
  124. data/lib/capybara/spec/session/has_field_spec.rb +34 -0
  125. data/lib/capybara/spec/session/has_select_spec.rb +32 -4
  126. data/lib/capybara/spec/session/has_selector_spec.rb +4 -4
  127. data/lib/capybara/spec/session/has_table_spec.rb +51 -5
  128. data/lib/capybara/spec/session/has_text_spec.rb +30 -0
  129. data/lib/capybara/spec/session/html_spec.rb +1 -1
  130. data/lib/capybara/spec/session/matches_style_spec.rb +2 -2
  131. data/lib/capybara/spec/session/node_spec.rb +394 -9
  132. data/lib/capybara/spec/session/refresh_spec.rb +2 -1
  133. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +2 -2
  134. data/lib/capybara/spec/session/save_page_spec.rb +4 -4
  135. data/lib/capybara/spec/session/save_screenshot_spec.rb +4 -15
  136. data/lib/capybara/spec/session/selectors_spec.rb +16 -3
  137. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +1 -1
  138. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +1 -1
  139. data/lib/capybara/spec/session/window/window_spec.rb +8 -8
  140. data/lib/capybara/spec/session/window/windows_spec.rb +1 -1
  141. data/lib/capybara/spec/spec_helper.rb +14 -14
  142. data/lib/capybara/spec/test_app.rb +27 -21
  143. data/lib/capybara/spec/views/form.erb +47 -4
  144. data/lib/capybara/spec/views/offset.erb +32 -0
  145. data/lib/capybara/spec/views/spatial.erb +31 -0
  146. data/lib/capybara/spec/views/with_animation.erb +37 -1
  147. data/lib/capybara/spec/views/with_dragula.erb +24 -0
  148. data/lib/capybara/spec/views/with_html.erb +24 -2
  149. data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
  150. data/lib/capybara/spec/views/with_js.erb +4 -1
  151. data/lib/capybara/spec/views/with_jstree.erb +26 -0
  152. data/lib/capybara/spec/views/with_sortable_js.erb +1 -1
  153. data/lib/capybara/version.rb +1 -1
  154. data/lib/capybara/window.rb +3 -7
  155. data/spec/basic_node_spec.rb +15 -14
  156. data/spec/capybara_spec.rb +28 -28
  157. data/spec/dsl_spec.rb +16 -3
  158. data/spec/filter_set_spec.rb +5 -5
  159. data/spec/fixtures/selenium_driver_rspec_failure.rb +1 -1
  160. data/spec/fixtures/selenium_driver_rspec_success.rb +1 -1
  161. data/spec/minitest_spec.rb +3 -2
  162. data/spec/minitest_spec_spec.rb +46 -46
  163. data/spec/rack_test_spec.rb +38 -15
  164. data/spec/regexp_dissassembler_spec.rb +52 -38
  165. data/spec/result_spec.rb +43 -32
  166. data/spec/rspec/features_spec.rb +4 -1
  167. data/spec/rspec/scenarios_spec.rb +4 -0
  168. data/spec/rspec/shared_spec_matchers.rb +68 -56
  169. data/spec/rspec_spec.rb +9 -5
  170. data/spec/selector_spec.rb +32 -17
  171. data/spec/selenium_spec_chrome.rb +78 -11
  172. data/spec/selenium_spec_chrome_remote.rb +23 -6
  173. data/spec/selenium_spec_edge.rb +15 -12
  174. data/spec/selenium_spec_firefox.rb +24 -19
  175. data/spec/selenium_spec_firefox_remote.rb +0 -8
  176. data/spec/selenium_spec_ie.rb +1 -6
  177. data/spec/server_spec.rb +106 -44
  178. data/spec/session_spec.rb +5 -5
  179. data/spec/shared_selenium_node.rb +56 -2
  180. data/spec/shared_selenium_session.rb +122 -15
  181. data/spec/spec_helper.rb +2 -2
  182. metadata +63 -17
  183. data/lib/capybara/spec/session/source_spec.rb +0 -0
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
@@ -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
@@ -76,13 +78,13 @@ RSpec.describe 'capybara/rspec' do
76
78
  context 'Type: Other', type: :other do
77
79
  context 'when RSpec::Matchers is included after Capybara::DSL' do
78
80
  let(:test_class_instance) do
79
- class DSLMatchersTest
81
+ Class.new do
80
82
  include Capybara::DSL
81
83
  include RSpec::Matchers
82
84
  end.new
83
85
  end
84
86
 
85
- context '#all' do
87
+ describe '#all' do
86
88
  it 'allows access to the Capybara finder' do
87
89
  test_class_instance.visit('/with_html')
88
90
  expect(test_class_instance.all(:css, 'h2.head').size).to eq(5)
@@ -95,7 +97,7 @@ RSpec.describe 'capybara/rspec' do
95
97
  end
96
98
  end
97
99
 
98
- context '#within' do
100
+ describe '#within' do
99
101
  it 'allows access to the Capybara scoper' do
100
102
  test_class_instance.visit('/with_html')
101
103
  expect do
@@ -143,3 +145,5 @@ feature 'Feature DSL' do
143
145
  expect(page.body).to include('Another World')
144
146
  end
145
147
  end
148
+
149
+ # rubocop:enable RSpec/MultipleDescribes
@@ -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>
@@ -69,12 +70,12 @@ RSpec.describe Capybara do
69
70
  end
70
71
 
71
72
  before do
72
- Capybara.add_selector :custom_selector do
73
+ described_class.add_selector :custom_selector do
73
74
  css { |css_class| "div.#{css_class}" }
74
75
  node_filter(:not_empty, boolean: true, default: true, skip_if: :all) { |node, value| value ^ (node.text == '') }
75
76
  end
76
77
 
77
- Capybara.add_selector :custom_css_selector do
78
+ described_class.add_selector :custom_css_selector do
78
79
  css(:name, :other_name) do |selector, name: nil, **|
79
80
  selector ||= ''
80
81
  selector += "[name='#{name}']" if name
@@ -94,7 +95,7 @@ RSpec.describe Capybara do
94
95
  end
95
96
  end
96
97
 
97
- Capybara.add_selector :custom_xpath_selector do
98
+ described_class.add_selector :custom_xpath_selector do
98
99
  xpath(:valid1, :valid2) { |selector| selector }
99
100
  match { |value| value == 'match_me' }
100
101
  end
@@ -102,7 +103,7 @@ RSpec.describe Capybara do
102
103
 
103
104
  it 'supports `filter` as an alias for `node_filter`' do
104
105
  expect do
105
- Capybara.add_selector :filter_alias_selector do
106
+ described_class.add_selector :filter_alias_selector do
106
107
  css { |_unused| 'div' }
107
108
  filter(:something) { |_node, _value| true }
108
109
  end
@@ -111,7 +112,7 @@ RSpec.describe Capybara do
111
112
 
112
113
  describe 'adding a selector' do
113
114
  it 'can set default visiblity' do
114
- Capybara.add_selector :hidden_field do
115
+ described_class.add_selector :hidden_field do
115
116
  visible :hidden
116
117
  css { |_sel| 'input[type="hidden"]' }
117
118
  end
@@ -125,7 +126,7 @@ RSpec.describe Capybara do
125
126
  it 'allows modifying a selector' do
126
127
  el = string.find(:custom_selector, 'aa')
127
128
  expect(el.tag_name).to eq 'div'
128
- Capybara.modify_selector :custom_selector do
129
+ described_class.modify_selector :custom_selector do
129
130
  css { |css_class| "h1.#{css_class}" }
130
131
  end
131
132
  el = string.find(:custom_selector, 'aa')
@@ -133,7 +134,7 @@ RSpec.describe Capybara do
133
134
  end
134
135
 
135
136
  it "doesn't change existing filters" do
136
- Capybara.modify_selector :custom_selector do
137
+ described_class.modify_selector :custom_selector do
137
138
  css { |css_class| "p.#{css_class}" }
138
139
  end
139
140
  expect(string).to have_selector(:custom_selector, 'bb', count: 1)
@@ -164,7 +165,7 @@ RSpec.describe Capybara do
164
165
 
165
166
  describe 'xpath' do
166
167
  it 'uses filter names passed in' do
167
- Capybara.add_selector :test do
168
+ described_class.add_selector :test do
168
169
  xpath(:something, :other) { |_locator| XPath.descendant }
169
170
  end
170
171
  selector = Capybara::Selector.new :test, config: nil, format: nil
@@ -173,7 +174,7 @@ RSpec.describe Capybara do
173
174
  end
174
175
 
175
176
  it 'gets filter names from block if none passed to xpath method' do
176
- Capybara.add_selector :test do
177
+ described_class.add_selector :test do
177
178
  xpath { |_locator, valid3:, valid4: nil| "#{valid3} #{valid4}" }
178
179
  end
179
180
  selector = Capybara::Selector.new :test, config: nil, format: nil
@@ -182,7 +183,7 @@ RSpec.describe Capybara do
182
183
  end
183
184
 
184
185
  it 'ignores block parameters if names passed in' do
185
- Capybara.add_selector :test do
186
+ described_class.add_selector :test do
186
187
  xpath(:valid1) { |_locator, valid3:, valid4: nil| "#{valid3} #{valid4}" }
187
188
  end
188
189
  selector = Capybara::Selector.new :test, config: nil, format: nil
@@ -205,7 +206,7 @@ RSpec.describe Capybara do
205
206
  end
206
207
 
207
208
  it 'uses filter names passed in' do
208
- Capybara.add_selector :test do
209
+ described_class.add_selector :test do
209
210
  css(:name, :other_name) { |_locator| '' }
210
211
  end
211
212
  selector = Capybara::Selector.new :test, config: nil, format: nil
@@ -214,7 +215,7 @@ RSpec.describe Capybara do
214
215
  end
215
216
 
216
217
  it 'gets filter names from block if none passed to css method' do
217
- Capybara.add_selector :test do
218
+ described_class.add_selector :test do
218
219
  css { |_locator, valid3:, valid4: nil| "#{valid3} #{valid4}" }
219
220
  end
220
221
  selector = Capybara::Selector.new :test, config: nil, format: nil
@@ -223,7 +224,7 @@ RSpec.describe Capybara do
223
224
  end
224
225
 
225
226
  it 'ignores block parameters if names passed in' do
226
- Capybara.add_selector :test do
227
+ described_class.add_selector :test do
227
228
  css(:valid1) { |_locator, valid3:, valid4: nil| "#{valid3} #{valid4}" }
228
229
  end
229
230
  selector = Capybara::Selector.new :test, config: nil, format: nil
@@ -326,9 +327,23 @@ RSpec.describe Capybara do
326
327
  expect(string.find(:custom_xpath_selector, './/div', class: /dOm WoR/i)[:id]).to eq 'random_words'
327
328
  end
328
329
 
329
- it 'accepts Regexp for CSS base selectors' do
330
+ it 'accepts Regexp for CSS based selectors' do
330
331
  expect(string.find(:custom_css_selector, 'div', class: /random/)[:id]).to eq 'random_words'
331
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
332
347
  end
333
348
 
334
349
  context 'with :style option' do
@@ -476,8 +491,8 @@ RSpec.describe Capybara do
476
491
  end
477
492
 
478
493
  describe ':link_or_button selector' do
479
- around(:all) do |example|
480
- Capybara.modify_selector(:link_or_button) do
494
+ around do |example|
495
+ described_class.modify_selector(:link_or_button) do
481
496
  expression_filter(:random) { |xpath, _| xpath } # do nothing filter
482
497
  end
483
498
  example.run
@@ -13,24 +13,62 @@ Selenium::WebDriver::Chrome.path = '/usr/bin/google-chrome-beta' if ENV['CI'] &&
13
13
  browser_options = ::Selenium::WebDriver::Chrome::Options.new
14
14
  browser_options.headless! if ENV['HEADLESS']
15
15
  browser_options.add_option(:w3c, ENV['W3C'] != 'false')
16
+ # Chromedriver 77 requires setting this for headless mode on linux
17
+ # Different versions of Chrome/selenium-webdriver require setting differently - jus set them all
18
+ browser_options.add_preference('download.default_directory', Capybara.save_path)
19
+ browser_options.add_preference(:download, default_directory: Capybara.save_path)
16
20
 
17
21
  Capybara.register_driver :selenium_chrome do |app|
18
- Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options, timeout: 30).tap do |driver|
22
+ version = Capybara::Selenium::Driver.load_selenium
23
+ options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
24
+ driver_options = { browser: :chrome, timeout: 30 }.tap do |opts|
25
+ opts[options_key] = browser_options
26
+ end
27
+
28
+ Capybara::Selenium::Driver.new(app, **driver_options).tap do |driver|
29
+ # Set download dir for Chrome < 77
19
30
  driver.browser.download_path = Capybara.save_path
20
31
  end
21
32
  end
22
33
 
23
34
  Capybara.register_driver :selenium_chrome_not_clear_storage do |app|
24
- chrome_options = {
25
- browser: :chrome,
26
- options: browser_options
27
- }
28
- Capybara::Selenium::Driver.new(app, chrome_options.merge(clear_local_storage: false, clear_session_storage: false))
35
+ version = Capybara::Selenium::Driver.load_selenium
36
+ options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
37
+ chrome_options = { browser: :chrome, clear_local_storage: false, clear_session_storage: false }.tap do |opts|
38
+ opts[options_key] = browser_options
39
+ end
40
+
41
+ Capybara::Selenium::Driver.new(app, **chrome_options)
42
+ end
43
+
44
+ Capybara.register_driver :selenium_chrome_not_clear_session_storage do |app|
45
+ version = Capybara::Selenium::Driver.load_selenium
46
+ options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
47
+ chrome_options = { browser: :chrome, clear_session_storage: false }.tap do |opts|
48
+ opts[options_key] = browser_options
49
+ end
50
+
51
+ Capybara::Selenium::Driver.new(app, **chrome_options)
52
+ end
53
+
54
+ Capybara.register_driver :selenium_chrome_not_clear_local_storage do |app|
55
+ version = Capybara::Selenium::Driver.load_selenium
56
+ options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
57
+ chrome_options = { browser: :chrome, clear_local_storage: false }.tap do |opts|
58
+ opts[options_key] = browser_options
59
+ end
60
+ Capybara::Selenium::Driver.new(app, **chrome_options)
29
61
  end
30
62
 
31
63
  Capybara.register_driver :selenium_driver_subclass_with_chrome do |app|
64
+ version = Capybara::Selenium::Driver.load_selenium
65
+ options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
32
66
  subclass = Class.new(Capybara::Selenium::Driver)
33
- subclass.new(app, browser: :chrome, options: browser_options, timeout: 30)
67
+ chrome_options = { browser: :chrome, timeout: 30 }.tap do |opts|
68
+ opts[options_key] = browser_options
69
+ end
70
+
71
+ subclass.new(app, **chrome_options)
34
72
  end
35
73
 
36
74
  module TestSessions
@@ -47,6 +85,10 @@ Capybara::SpecHelper.run_specs TestSessions::Chrome, CHROME_DRIVER.to_s, capybar
47
85
  skip 'Need to figure out testing of file downloading on windows platform' if Gem.win_platform?
48
86
  when /Capybara::Session selenium_chrome Capybara::Window#maximize/
49
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'
50
92
  end
51
93
  end
52
94
 
@@ -77,6 +119,26 @@ RSpec.describe 'Capybara::Session with chrome' do
77
119
  expect(session.evaluate_script('Object.keys(localStorage)')).not_to be_empty
78
120
  expect(session.evaluate_script('Object.keys(sessionStorage)')).not_to be_empty
79
121
  end
122
+
123
+ it 'can not clear session storage' do
124
+ session = Capybara::Session.new(:selenium_chrome_not_clear_session_storage, TestApp)
125
+ session.visit('/with_js')
126
+ session.find(:css, '#set-storage').click
127
+ session.reset!
128
+ session.visit('/with_js')
129
+ expect(session.evaluate_script('Object.keys(localStorage)')).to be_empty
130
+ expect(session.evaluate_script('Object.keys(sessionStorage)')).not_to be_empty
131
+ end
132
+
133
+ it 'can not clear local storage' do
134
+ session = Capybara::Session.new(:selenium_chrome_not_clear_local_storage, TestApp)
135
+ session.visit('/with_js')
136
+ session.find(:css, '#set-storage').click
137
+ session.reset!
138
+ session.visit('/with_js')
139
+ expect(session.evaluate_script('Object.keys(localStorage)')).not_to be_empty
140
+ expect(session.evaluate_script('Object.keys(sessionStorage)')).to be_empty
141
+ end
80
142
  end
81
143
  end
82
144
 
@@ -125,16 +187,21 @@ RSpec.describe 'Capybara::Session with chrome' do
125
187
  describe 'log access' do
126
188
  before { skip 'Only makes sense in W3C mode' if ENV['W3C'] == 'false' }
127
189
 
128
- it 'errors when getting log types' do
190
+ it 'does not error getting log types' do
191
+ skip if Gem::Requirement.new('< 75.0.3770.90').satisfied_by? chromedriver_version
129
192
  expect do
130
193
  session.driver.browser.manage.logs.available_types
131
- end.to raise_error(NotImplementedError, /Chromedriver 75\+ defaults to W3C mode/)
194
+ end.not_to raise_error
132
195
  end
133
196
 
134
- it 'errors when getting logs' do
197
+ it 'does not error when getting logs' do
135
198
  expect do
136
199
  session.driver.browser.manage.logs.get(:browser)
137
- end.to raise_error(NotImplementedError, /Chromedriver 75\+ defaults to W3C mode/)
200
+ end.not_to raise_error
138
201
  end
139
202
  end
203
+
204
+ def chromedriver_version
205
+ Gem::Version.new(session.driver.browser.capabilities['chrome']['chromedriverVersion'].split[0])
206
+ end
140
207
  end
@@ -54,12 +54,6 @@ module TestSessions
54
54
  Chrome = Capybara::Session.new(CHROME_REMOTE_DRIVER, TestApp)
55
55
  end
56
56
 
57
- TestSessions::Chrome.driver.browser.file_detector = lambda do |args|
58
- # args => ["/path/to/file"]
59
- str = args.first.to_s
60
- str if File.exist?(str)
61
- end
62
-
63
57
  skipped_tests = %i[response_headers status_code trigger download]
64
58
 
65
59
  Capybara::SpecHelper.run_specs TestSessions::Chrome, CHROME_REMOTE_DRIVER.to_s, capybara_skip: skipped_tests do |example|
@@ -68,6 +62,8 @@ Capybara::SpecHelper.run_specs TestSessions::Chrome, CHROME_REMOTE_DRIVER.to_s,
68
62
  'Capybara::Session selenium_chrome_remote #attach_file with multipart form should fire change once for each set of files uploaded',
69
63
  'Capybara::Session selenium_chrome_remote #attach_file with multipart form should fire change once when uploading multiple files from empty'
70
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'
71
67
  end
72
68
  end
73
69
 
@@ -80,4 +76,25 @@ RSpec.describe 'Capybara::Session with remote Chrome' do
80
76
  it 'is considered to be chrome' do
81
77
  expect(session.driver.browser.browser).to eq :chrome
82
78
  end
79
+
80
+ describe 'log access' do
81
+ before { skip 'Only makes sense in W3C mode' if ENV['W3C'] == 'false' }
82
+
83
+ it 'does not error when getting log types' do
84
+ skip unless Gem::Requirement.new('>= 75.0.3770.90').satisfied_by? chromedriver_version
85
+ expect do
86
+ session.driver.browser.manage.logs.available_types
87
+ end.not_to raise_error
88
+ end
89
+
90
+ it 'does not error when getting logs' do
91
+ expect do
92
+ session.driver.browser.manage.logs.get(:browser)
93
+ end.not_to raise_error
94
+ end
95
+ end
96
+
97
+ def chromedriver_version
98
+ Gem::Version.new(session.driver.browser.capabilities['chrome']['chromedriverVersion'].split[0])
99
+ end
83
100
  end
@@ -6,12 +6,19 @@ require 'shared_selenium_session'
6
6
  require 'shared_selenium_node'
7
7
  require 'rspec/shared_spec_matchers'
8
8
 
9
- Selenium::WebDriver::Edge::Service.driver_path = '/usr/local/bin/msedgedriver'
10
- Selenium::WebDriver::EdgeChrome.path = '/Applications/Microsoft Edge Canary.app/Contents/MacOS/Microsoft Edge Canary'
9
+ # unless ENV['CI']
10
+ # Selenium::WebDriver::Edge::Service.driver_path = '/usr/local/bin/msedgedriver'
11
+ # end
12
+
13
+ if ::Selenium::WebDriver::Platform.mac?
14
+ Selenium::WebDriver::EdgeChrome.path = '/Applications/Microsoft Edge Dev.app/Contents/MacOS/Microsoft Edge Dev'
15
+ end
11
16
 
12
17
  Capybara.register_driver :selenium_edge do |app|
13
18
  # ::Selenium::WebDriver.logger.level = "debug"
14
- Capybara::Selenium::Driver.new(app, browser: :edge_chrome).tap do |driver|
19
+ # 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|
15
22
  driver.browser
16
23
  driver.download_path = Capybara.save_path
17
24
  end
@@ -23,17 +30,13 @@ end
23
30
 
24
31
  skipped_tests = %i[response_headers status_code trigger]
25
32
 
26
- Capybara::SpecHelper.log_selenium_driver_version(Selenium::WebDriver::Edge) if ENV['CI']
33
+ Capybara::SpecHelper.log_selenium_driver_version(Selenium::WebDriver::EdgeChrome) if ENV['CI']
27
34
 
28
35
  Capybara::SpecHelper.run_specs TestSessions::SeleniumEdge, 'selenium', capybara_skip: skipped_tests do |example|
29
- # case example.metadata[:description]
30
- # when /#refresh it reposts$/
31
- # skip 'Edge insists on prompting without providing a way to suppress'
32
- # when /should be able to open non-http url/
33
- # skip 'Crashes'
34
- # when /when Capybara.always_include_port is true/
35
- # skip 'Crashes'
36
- # end
36
+ case example.metadata[:full_description]
37
+ 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"
39
+ end
37
40
  end
38
41
 
39
42
  RSpec.describe 'Capybara::Session with Edge', capybara_skip: skipped_tests do
@@ -14,28 +14,30 @@ browser_options.profile = Selenium::WebDriver::Firefox::Profile.new.tap do |prof
14
14
  profile['browser.download.dir'] = Capybara.save_path
15
15
  profile['browser.download.folderList'] = 2
16
16
  profile['browser.helperApps.neverAsk.saveToDisk'] = 'text/csv'
17
+ profile['browser.startup.homepage'] = 'about:blank' # workaround bug in Selenium 4 alpha4-7
17
18
  end
18
19
 
19
20
  Capybara.register_driver :selenium_firefox do |app|
20
21
  # ::Selenium::WebDriver.logger.level = "debug"
21
- Capybara::Selenium::Driver.new(
22
- app,
23
- browser: :firefox,
24
- options: browser_options,
25
- timeout: 31
22
+ version = Capybara::Selenium::Driver.load_selenium
23
+ options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
24
+ driver_options = { browser: :firefox, timeout: 31 }.tap do |opts|
25
+ opts[options_key] = browser_options
26
26
  # Get a trace level log from geckodriver
27
27
  # :driver_opts => { args: ['-vv'] }
28
- )
28
+ end
29
+
30
+ Capybara::Selenium::Driver.new(app, **driver_options)
29
31
  end
30
32
 
31
33
  Capybara.register_driver :selenium_firefox_not_clear_storage do |app|
32
- Capybara::Selenium::Driver.new(
33
- app,
34
- browser: :firefox,
35
- clear_local_storage: false,
36
- clear_session_storage: false,
37
- options: browser_options
38
- )
34
+ version = Capybara::Selenium::Driver.load_selenium
35
+ options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
36
+ driver_options = { browser: :firefox, clear_local_storage: false, clear_session_storage: false }.tap do |opts|
37
+ opts[options_key] = browser_options
38
+ end
39
+
40
+ Capybara::Selenium::Driver.new(app, **driver_options)
39
41
  end
40
42
 
41
43
  module TestSessions
@@ -49,7 +51,7 @@ Capybara::SpecHelper.log_selenium_driver_version(Selenium::WebDriver::Firefox) i
49
51
  Capybara::SpecHelper.run_specs TestSessions::SeleniumFirefox, 'selenium', capybara_skip: skipped_tests do |example|
50
52
  case example.metadata[:full_description]
51
53
  when 'Capybara::Session selenium node #click should allow multiple modifiers'
52
- pending "Firefox doesn't generate an event for shift+control+click" if firefox_gte?(62, @session) && !Gem.win_platform?
54
+ pending "Firefox on OSX doesn't generate an event for shift+control+click" if firefox_gte?(62, @session) && Selenium::WebDriver::Platform.mac?
53
55
  when /^Capybara::Session selenium node #double_click/
54
56
  pending "selenium-webdriver/geckodriver doesn't generate double click event" if firefox_lt?(59, @session)
55
57
  when 'Capybara::Session selenium #accept_prompt should accept the prompt with a blank response when there is a default'
@@ -57,15 +59,18 @@ Capybara::SpecHelper.run_specs TestSessions::SeleniumFirefox, 'selenium', capyba
57
59
  when 'Capybara::Session selenium #attach_file with multipart form should fire change once when uploading multiple files from empty'
58
60
  pending "FF < 62 doesn't support setting all files at once" if firefox_lt?(62, @session)
59
61
  when 'Capybara::Session selenium #accept_confirm should work with nested modals'
60
- skip 'Broken in FF 63 - https://bugzilla.mozilla.org/show_bug.cgi?id=1487358' if firefox_gte?(63, @session)
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'
61
65
  when 'Capybara::Session selenium #click_link can download a file'
62
66
  skip 'Need to figure out testing of file downloading on windows platform' if Gem.win_platform?
63
67
  when 'Capybara::Session selenium #reset_session! removes ALL cookies'
64
68
  pending "Geckodriver doesn't provide a way to remove cookies outside the current domain"
65
- when 'Capybara::Session selenium #attach_file with a block can upload by clicking the file input'
66
- pending "Geckodriver doesn't allow clicking on file inputs"
67
69
  when /drag_to.*HTML5/
68
70
  pending "Firefox < 62 doesn't support a DataTransfer constuctor" if firefox_lt?(62.0, @session)
71
+ when 'Capybara::Session selenium #accept_alert should handle the alert if the page changes',
72
+ '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'
69
74
  end
70
75
  end
71
76
 
@@ -106,7 +111,7 @@ RSpec.describe 'Capybara::Session with firefox' do # rubocop:disable RSpec/Multi
106
111
  end
107
112
 
108
113
  RSpec.describe Capybara::Selenium::Driver do
109
- let(:driver) { Capybara::Selenium::Driver.new(TestApp, browser: :firefox, options: browser_options) }
114
+ let(:driver) { described_class.new(TestApp, browser: :firefox, options: browser_options) }
110
115
 
111
116
  describe '#quit' do
112
117
  it 'should reset browser when quit' do
@@ -183,7 +188,7 @@ RSpec.describe Capybara::Selenium::Driver do
183
188
  end
184
189
 
185
190
  RSpec.describe Capybara::Selenium::Node do
186
- context '#click' do
191
+ describe '#click' do
187
192
  it 'warns when attempting on a table row' do
188
193
  session = TestSessions::SeleniumFirefox
189
194
  session.visit('/tables')