capybara 3.23.0 → 3.35.3

Sign up to get free protection for your applications and to get access to all the features.
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')