capybara 3.32.0 → 3.35.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 (125) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +99 -15
  3. data/README.md +9 -4
  4. data/lib/capybara.rb +18 -8
  5. data/lib/capybara/config.rb +4 -6
  6. data/lib/capybara/cucumber.rb +1 -1
  7. data/lib/capybara/driver/base.rb +4 -0
  8. data/lib/capybara/helpers.rb +25 -1
  9. data/lib/capybara/minitest.rb +2 -3
  10. data/lib/capybara/minitest/spec.rb +14 -11
  11. data/lib/capybara/node/actions.rb +16 -21
  12. data/lib/capybara/node/base.rb +6 -6
  13. data/lib/capybara/node/element.rb +1 -5
  14. data/lib/capybara/node/finders.rb +7 -6
  15. data/lib/capybara/node/matchers.rb +12 -12
  16. data/lib/capybara/node/simple.rb +5 -1
  17. data/lib/capybara/queries/ancestor_query.rb +1 -1
  18. data/lib/capybara/queries/current_path_query.rb +14 -4
  19. data/lib/capybara/queries/selector_query.rb +40 -18
  20. data/lib/capybara/queries/sibling_query.rb +1 -1
  21. data/lib/capybara/queries/style_query.rb +1 -1
  22. data/lib/capybara/queries/text_query.rb +7 -1
  23. data/lib/capybara/rack_test/browser.rb +7 -3
  24. data/lib/capybara/rack_test/driver.rb +1 -0
  25. data/lib/capybara/rack_test/form.rb +1 -1
  26. data/lib/capybara/rack_test/node.rb +1 -1
  27. data/lib/capybara/registration_container.rb +44 -0
  28. data/lib/capybara/registrations/drivers.rb +18 -12
  29. data/lib/capybara/registrations/patches/puma_ssl.rb +3 -1
  30. data/lib/capybara/registrations/servers.rb +3 -2
  31. data/lib/capybara/result.rb +10 -11
  32. data/lib/capybara/rspec.rb +2 -0
  33. data/lib/capybara/rspec/matcher_proxies.rb +1 -1
  34. data/lib/capybara/rspec/matchers.rb +7 -6
  35. data/lib/capybara/rspec/matchers/have_current_path.rb +2 -2
  36. data/lib/capybara/rspec/matchers/have_text.rb +1 -1
  37. data/lib/capybara/rspec/matchers/match_style.rb +5 -0
  38. data/lib/capybara/selector.rb +12 -3
  39. data/lib/capybara/selector/builders/css_builder.rb +1 -1
  40. data/lib/capybara/selector/builders/xpath_builder.rb +3 -1
  41. data/lib/capybara/selector/definition.rb +11 -9
  42. data/lib/capybara/selector/definition/button.rb +26 -14
  43. data/lib/capybara/selector/definition/css.rb +1 -1
  44. data/lib/capybara/selector/definition/datalist_input.rb +1 -1
  45. data/lib/capybara/selector/definition/element.rb +2 -1
  46. data/lib/capybara/selector/definition/fillable_field.rb +1 -1
  47. data/lib/capybara/selector/definition/label.rb +1 -1
  48. data/lib/capybara/selector/definition/link.rb +8 -0
  49. data/lib/capybara/selector/definition/select.rb +1 -1
  50. data/lib/capybara/selector/definition/table.rb +1 -1
  51. data/lib/capybara/selector/definition/table_row.rb +2 -2
  52. data/lib/capybara/selector/filter_set.rb +2 -2
  53. data/lib/capybara/selector/selector.rb +9 -1
  54. data/lib/capybara/selenium/atoms/src/isDisplayed.js +1 -1
  55. data/lib/capybara/selenium/driver.rb +51 -7
  56. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +9 -11
  57. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +9 -11
  58. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +3 -3
  59. data/lib/capybara/selenium/extensions/find.rb +4 -4
  60. data/lib/capybara/selenium/extensions/scroll.rb +8 -10
  61. data/lib/capybara/selenium/logger_suppressor.rb +1 -1
  62. data/lib/capybara/selenium/node.rb +23 -6
  63. data/lib/capybara/selenium/nodes/chrome_node.rb +23 -5
  64. data/lib/capybara/selenium/nodes/firefox_node.rb +7 -2
  65. data/lib/capybara/selenium/nodes/safari_node.rb +1 -1
  66. data/lib/capybara/selenium/patches/action_pauser.rb +4 -1
  67. data/lib/capybara/selenium/patches/atoms.rb +4 -4
  68. data/lib/capybara/selenium/patches/logs.rb +7 -9
  69. data/lib/capybara/server/animation_disabler.rb +8 -3
  70. data/lib/capybara/server/middleware.rb +4 -2
  71. data/lib/capybara/session.rb +23 -14
  72. data/lib/capybara/session/config.rb +3 -1
  73. data/lib/capybara/session/matchers.rb +11 -11
  74. data/lib/capybara/spec/public/test.js +13 -1
  75. data/lib/capybara/spec/session/accept_alert_spec.rb +1 -1
  76. data/lib/capybara/spec/session/check_spec.rb +6 -0
  77. data/lib/capybara/spec/session/click_button_spec.rb +11 -0
  78. data/lib/capybara/spec/session/current_url_spec.rb +11 -1
  79. data/lib/capybara/spec/session/has_button_spec.rb +51 -0
  80. data/lib/capybara/spec/session/has_css_spec.rb +2 -1
  81. data/lib/capybara/spec/session/has_current_path_spec.rb +15 -2
  82. data/lib/capybara/spec/session/has_field_spec.rb +16 -0
  83. data/lib/capybara/spec/session/has_select_spec.rb +4 -4
  84. data/lib/capybara/spec/session/has_selector_spec.rb +4 -4
  85. data/lib/capybara/spec/session/has_text_spec.rb +0 -11
  86. data/lib/capybara/spec/session/html_spec.rb +1 -1
  87. data/lib/capybara/spec/session/matches_style_spec.rb +2 -2
  88. data/lib/capybara/spec/session/node_spec.rb +29 -9
  89. data/lib/capybara/spec/session/refresh_spec.rb +2 -1
  90. data/lib/capybara/spec/session/save_page_spec.rb +4 -4
  91. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +1 -1
  92. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +1 -1
  93. data/lib/capybara/spec/session/window/window_spec.rb +1 -1
  94. data/lib/capybara/spec/session/window/windows_spec.rb +1 -1
  95. data/lib/capybara/spec/spec_helper.rb +12 -12
  96. data/lib/capybara/spec/test_app.rb +23 -21
  97. data/lib/capybara/spec/views/form.erb +28 -1
  98. data/lib/capybara/spec/views/with_animation.erb +8 -0
  99. data/lib/capybara/spec/views/with_dragula.erb +3 -1
  100. data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
  101. data/lib/capybara/spec/views/with_js.erb +3 -0
  102. data/lib/capybara/spec/views/with_sortable_js.erb +1 -1
  103. data/lib/capybara/version.rb +1 -1
  104. data/lib/capybara/window.rb +3 -7
  105. data/spec/basic_node_spec.rb +9 -8
  106. data/spec/capybara_spec.rb +1 -1
  107. data/spec/dsl_spec.rb +14 -1
  108. data/spec/fixtures/selenium_driver_rspec_success.rb +1 -1
  109. data/spec/minitest_spec.rb +3 -2
  110. data/spec/rack_test_spec.rb +16 -5
  111. data/spec/result_spec.rb +1 -17
  112. data/spec/rspec/features_spec.rb +3 -1
  113. data/spec/rspec/scenarios_spec.rb +4 -0
  114. data/spec/rspec/shared_spec_matchers.rb +63 -51
  115. data/spec/rspec_spec.rb +4 -0
  116. data/spec/selector_spec.rb +17 -2
  117. data/spec/selenium_spec_chrome.rb +39 -20
  118. data/spec/selenium_spec_chrome_remote.rb +5 -1
  119. data/spec/selenium_spec_firefox.rb +15 -13
  120. data/spec/server_spec.rb +60 -49
  121. data/spec/shared_selenium_node.rb +10 -0
  122. data/spec/shared_selenium_session.rb +98 -7
  123. data/spec/spec_helper.rb +1 -1
  124. metadata +50 -15
  125. data/lib/capybara/spec/session/source_spec.rb +0 -0
@@ -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
@@ -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,6 +3,7 @@
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
9
  described_class.string <<-STRING
@@ -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,7 +491,7 @@ RSpec.describe Capybara do
476
491
  end
477
492
 
478
493
  describe ':link_or_button selector' do
479
- around(:all) do |example|
494
+ around do |example|
480
495
  described_class.modify_selector(:link_or_button) do
481
496
  expression_filter(:random) { |xpath, _| xpath } # do nothing filter
482
497
  end
@@ -19,39 +19,56 @@ browser_options.add_preference('download.default_directory', Capybara.save_path)
19
19
  browser_options.add_preference(:download, default_directory: Capybara.save_path)
20
20
 
21
21
  Capybara.register_driver :selenium_chrome do |app|
22
- 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|
23
29
  # Set download dir for Chrome < 77
24
30
  driver.browser.download_path = Capybara.save_path
25
31
  end
26
32
  end
27
33
 
28
34
  Capybara.register_driver :selenium_chrome_not_clear_storage do |app|
29
- chrome_options = {
30
- browser: :chrome,
31
- options: browser_options
32
- }
33
- 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)
34
42
  end
35
43
 
36
44
  Capybara.register_driver :selenium_chrome_not_clear_session_storage do |app|
37
- chrome_options = {
38
- browser: :chrome,
39
- options: browser_options
40
- }
41
- Capybara::Selenium::Driver.new(app, **chrome_options.merge(clear_session_storage: false))
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)
42
52
  end
43
53
 
44
54
  Capybara.register_driver :selenium_chrome_not_clear_local_storage do |app|
45
- chrome_options = {
46
- browser: :chrome,
47
- options: browser_options
48
- }
49
- Capybara::Selenium::Driver.new(app, **chrome_options.merge(clear_local_storage: false))
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)
50
61
  end
51
62
 
52
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
53
66
  subclass = Class.new(Capybara::Selenium::Driver)
54
- 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)
55
72
  end
56
73
 
57
74
  module TestSessions
@@ -70,8 +87,6 @@ Capybara::SpecHelper.run_specs TestSessions::Chrome, CHROME_DRIVER.to_s, capybar
70
87
  pending "Chrome headless doesn't support maximize" if ENV['HEADLESS']
71
88
  when /Capybara::Window#fullscreen should be able to fullscreen the window/
72
89
  skip 'Chromedriver hangs on attempts to fullscreen in headless mode' if ENV['HEADLESS']
73
- when /details non-summary descendants should be non-visible/
74
- pending 'Chromedriver built-in is_displayed is currently broken' if ENV['W3C'] == 'false'
75
90
  when /node #right_click delay should delay the mouse up/
76
91
  skip "Legacy selenium doesn't support separate right button down/up" if ENV['W3C'] == 'false'
77
92
  end
@@ -173,7 +188,7 @@ RSpec.describe 'Capybara::Session with chrome' do
173
188
  before { skip 'Only makes sense in W3C mode' if ENV['W3C'] == 'false' }
174
189
 
175
190
  it 'does not error getting log types' do
176
- skip if Gem::Version.new(session.driver.browser.capabilities['chrome']['chromedriverVersion'].split[0]) < Gem::Version.new('75.0.3770.90')
191
+ skip if Gem::Requirement.new('< 75.0.3770.90').satisfied_by? chromedriver_version
177
192
  expect do
178
193
  session.driver.browser.manage.logs.available_types
179
194
  end.not_to raise_error
@@ -185,4 +200,8 @@ RSpec.describe 'Capybara::Session with chrome' do
185
200
  end.not_to raise_error
186
201
  end
187
202
  end
203
+
204
+ def chromedriver_version
205
+ Gem::Version.new(session.driver.browser.capabilities['chrome']['chromedriverVersion'].split[0])
206
+ end
188
207
  end
@@ -81,7 +81,7 @@ RSpec.describe 'Capybara::Session with remote Chrome' do
81
81
  before { skip 'Only makes sense in W3C mode' if ENV['W3C'] == 'false' }
82
82
 
83
83
  it 'does not error when getting log types' do
84
- skip if Gem::Version.new(session.driver.browser.capabilities['chrome']['chromedriverVersion'].split[0]) < Gem::Version.new('75.0.3770.90')
84
+ skip unless Gem::Requirement.new('>= 75.0.3770.90').satisfied_by? chromedriver_version
85
85
  expect do
86
86
  session.driver.browser.manage.logs.available_types
87
87
  end.not_to raise_error
@@ -93,4 +93,8 @@ RSpec.describe 'Capybara::Session with remote Chrome' do
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
96
100
  end
@@ -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
@@ -75,25 +75,21 @@ RSpec.describe Capybara::Server do
75
75
  end
76
76
 
77
77
  it 'should handle that getting available ports fails randomly' do
78
- begin
79
- # Use a port to force a EADDRINUSE error to be generated
80
- server = TCPServer.new('0.0.0.0', 0)
81
- server_port = server.addr[1]
82
- d_server = instance_double('TCPServer', addr: [nil, server_port, nil, nil], close: nil)
83
- call_count = 0
84
- allow(TCPServer).to receive(:new).and_wrap_original do |m, *args|
85
- begin
86
- call_count.zero? ? d_server : m.call(*args)
87
- ensure
88
- call_count += 1
89
- end
90
- end
91
-
92
- port = described_class.new(Object.new, host: '0.0.0.0').port
93
- expect(port).not_to eq(server_port)
78
+ # Use a port to force a EADDRINUSE error to be generated
79
+ server = TCPServer.new('0.0.0.0', 0)
80
+ server_port = server.addr[1]
81
+ d_server = instance_double('TCPServer', addr: [nil, server_port, nil, nil], close: nil)
82
+ call_count = 0
83
+ allow(TCPServer).to receive(:new).and_wrap_original do |m, *args|
84
+ call_count.zero? ? d_server : m.call(*args)
94
85
  ensure
95
- server&.close
86
+ call_count += 1
96
87
  end
88
+
89
+ port = described_class.new(Object.new, host: '0.0.0.0').port
90
+ expect(port).not_to eq(server_port)
91
+ ensure
92
+ server&.close
97
93
  end
98
94
 
99
95
  it 'should return its #base_url' do
@@ -103,30 +99,47 @@ RSpec.describe Capybara::Server do
103
99
  expect(uri.to_hash).to include(scheme: 'http', host: server.host, port: server.port)
104
100
  end
105
101
 
106
- it 'should support SSL' do
107
- begin
108
- key = File.join(Dir.pwd, 'spec', 'fixtures', 'key.pem')
109
- cert = File.join(Dir.pwd, 'spec', 'fixtures', 'certificate.pem')
110
- Capybara.server = :puma, { Host: "ssl://#{Capybara.server_host}?key=#{key}&cert=#{cert}" }
111
- app = proc { |_env| [200, {}, ['Hello SSL Server!']] }
112
- server = described_class.new(app).boot
102
+ it 'should call #clamp on the puma configuration to ensure that environment is a string' do
103
+ Capybara.server = :puma
104
+ app_proc = proc { |_env| [200, {}, ['Hello Puma!']] }
105
+ require 'puma'
106
+ allow(Puma::Server).to receive(:new).and_wrap_original do |method, app, events, options|
107
+ # If #clamp is not called on the puma config then this will be a Proc
108
+ expect(options.fetch(:environment)).to be_a(String)
109
+ method.call(app, events, options)
110
+ end
111
+ server = described_class.new(app_proc).boot
112
+ expect(Puma::Server).to have_received(:new).with(
113
+ anything,
114
+ anything,
115
+ satisfy { |opts| opts.final_options[:Port] == server.port }
116
+ )
117
+ ensure
118
+ Capybara.server = :default
119
+ end
113
120
 
114
- expect do
115
- Net::HTTP.start(server.host, server.port, max_retries: 0) { |http| http.get('/__identify__') }
116
- end.to(raise_error do |e|
117
- expect(e.is_a?(EOFError) || e.is_a?(Net::ReadTimeout)).to be true
118
- end)
121
+ it 'should support SSL' do
122
+ key = File.join(Dir.pwd, 'spec', 'fixtures', 'key.pem')
123
+ cert = File.join(Dir.pwd, 'spec', 'fixtures', 'certificate.pem')
124
+ Capybara.server = :puma, { Host: "ssl://#{Capybara.server_host}?key=#{key}&cert=#{cert}" }
125
+ app = proc { |_env| [200, {}, ['Hello SSL Server!']] }
126
+ server = described_class.new(app).boot
119
127
 
120
- res = Net::HTTP.start(server.host, server.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) do |https|
121
- https.get('/')
122
- end
128
+ expect do
129
+ Net::HTTP.start(server.host, server.port, max_retries: 0) { |http| http.get('/__identify__') }
130
+ end.to(raise_error do |e|
131
+ expect(e.is_a?(EOFError) || e.is_a?(Net::ReadTimeout)).to be true
132
+ end)
123
133
 
124
- expect(res.body).to include('Hello SSL Server!')
125
- uri = ::Addressable::URI.parse(server.base_url)
126
- expect(uri.to_hash).to include(scheme: 'https', host: server.host, port: server.port)
127
- ensure
128
- Capybara.server = :default
134
+ res = Net::HTTP.start(server.host, server.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) do |https|
135
+ https.get('/')
129
136
  end
137
+
138
+ expect(res.body).to include('Hello SSL Server!')
139
+ uri = ::Addressable::URI.parse(server.base_url)
140
+ expect(uri.to_hash).to include(scheme: 'https', host: server.host, port: server.port)
141
+ ensure
142
+ Capybara.server = :default
130
143
  end
131
144
 
132
145
  context 'When Capybara.reuse_server is true' do
@@ -224,19 +237,17 @@ RSpec.describe Capybara::Server do
224
237
  end
225
238
 
226
239
  it 'should raise server errors when the server errors before the timeout' do
227
- begin
228
- Capybara.register_server :kaboom do
229
- sleep 0.1
230
- raise 'kaboom'
231
- end
232
- Capybara.server = :kaboom
233
-
234
- expect do
235
- described_class.new(proc { |e| }).boot
236
- end.to raise_error(RuntimeError, 'kaboom')
237
- ensure
238
- Capybara.server = :default
240
+ Capybara.register_server :kaboom do
241
+ sleep 0.1
242
+ raise 'kaboom'
239
243
  end
244
+ Capybara.server = :kaboom
245
+
246
+ expect do
247
+ described_class.new(proc { |e| }).boot
248
+ end.to raise_error(RuntimeError, 'kaboom')
249
+ ensure
250
+ Capybara.server = :default
240
251
  end
241
252
 
242
253
  it 'should raise an error when there are pending requests' do
@@ -35,6 +35,16 @@ RSpec.shared_examples 'Capybara::Node' do |session, _mode|
35
35
  end
36
36
  end
37
37
 
38
+ describe '#set' do
39
+ it 'respects maxlength when using rapid set' do
40
+ session.visit('/form')
41
+ inp = session.find(:css, '#long_length')
42
+ value = (0...50).map { |i| ((i % 26) + 65).chr }.join
43
+ inp.set(value, rapid: true)
44
+ expect(inp.value).to eq value[0...35]
45
+ end
46
+ end
47
+
38
48
  describe '#visible?' do
39
49
  let(:bridge) do
40
50
  session.driver.browser.send(:bridge)
@@ -160,6 +160,15 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
160
160
  fill_options: { clear: :none })
161
161
  expect(session.find(:fillable_field, 'form_first_name').value).to eq('JohnHarry')
162
162
  end
163
+
164
+ it 'works with rapid fill' do
165
+ pending 'Safari overwrites by default - need to figure out a workaround' if safari?(session)
166
+
167
+ long_string = (0...60).map { |i| ((i % 26) + 65).chr }.join
168
+ session.visit('/form')
169
+ session.fill_in('form_first_name', with: long_string, fill_options: { clear: :none })
170
+ expect(session.find(:fillable_field, 'form_first_name').value).to eq("John#{long_string}")
171
+ end
163
172
  end
164
173
 
165
174
  describe '#fill_in with Date' do
@@ -205,6 +214,26 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
205
214
  end
206
215
  end
207
216
 
217
+ describe '#fill_in with Emoji' do
218
+ it 'sends emojis' do
219
+ session.visit('/form')
220
+ session.fill_in('form_first_name', with: 'a😀cd😴 🛌🏽🇵🇹 e🤾🏽‍♀️f')
221
+ expect(session.find(:fillable_field, 'form_first_name').value).to eq('a😀cd😴 🛌🏽🇵🇹 e🤾🏽‍♀️f')
222
+ end
223
+ end
224
+
225
+ describe '#send_keys' do
226
+ it 'defaults to sending keys to the document.activeElement' do
227
+ session.visit('/form')
228
+
229
+ expect(session.evaluate_script('document.activeElement')).to eq(session.find('//body'))
230
+
231
+ session.send_keys(:tab)
232
+
233
+ expect(session.evaluate_script('document.activeElement')).to eq(session.first(:field))
234
+ end
235
+ end
236
+
208
237
  describe '#path' do
209
238
  it 'returns xpath' do
210
239
  # this is here because it is testing for an XPath that is specific to the algorithm used in the selenium driver
@@ -360,15 +389,64 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
360
389
 
361
390
  it 'should disable CSS animations (set to 0s)' do
362
391
  @animation_session.visit('with_animation')
392
+ sleep 1
363
393
  @animation_session.click_link('animate me away')
364
394
  expect(@animation_session).to have_no_link('animate me away', wait: 0.5)
365
395
  end
366
396
 
367
397
  it 'should disable CSS animations on pseudo elements (set to 0s)' do
368
398
  @animation_session.visit('with_animation')
399
+ sleep 1
369
400
  @animation_session.find_link('animate me away').right_click
370
401
  expect(@animation_session).to have_content('Animation Ended', wait: 0.1)
371
402
  end
403
+
404
+ it 'should scroll the page instantly', requires: [:js] do
405
+ @animation_session.visit('with_animation')
406
+ scroll_y = @animation_session.evaluate_script(<<~JS)
407
+ (function(){
408
+ window.scrollTo(0,500);
409
+ return window.scrollY;
410
+ })()
411
+ JS
412
+ expect(scroll_y).to eq 500
413
+ end
414
+
415
+ it 'should scroll the page instantly without jquery animation', requires: [:js] do
416
+ @animation_session.visit('with_jquery_animation')
417
+ @animation_session.click_link('scroll top 500')
418
+ scroll_y = @animation_session.evaluate_script('window.scrollY')
419
+ expect(scroll_y).to eq 500
420
+ end
421
+ end
422
+
423
+ context 'when set to `false`' do
424
+ before(:context) do # rubocop:disable RSpec/BeforeAfterAll
425
+ skip "Safari doesn't support multiple sessions" if safari?(session)
426
+ # NOTE: Although Capybara.SpecHelper.reset! sets Capybara.disable_animation to false,
427
+ # it doesn't affect any of these tests because the settings are applied per-session
428
+ Capybara.disable_animation = false
429
+ @animation_session = Capybara::Session.new(session.mode, TestApp.new)
430
+ end
431
+
432
+ it 'should scroll the page with a smooth animation', requires: [:js] do
433
+ @animation_session.visit('with_animation')
434
+ scroll_y = @animation_session.evaluate_script(<<~JS)
435
+ (function(){
436
+ window.scrollTo(0,500);
437
+ return window.scrollY;
438
+ })()
439
+ JS
440
+ # measured over 0.5 seconds: 0, 75, 282, 478, 500
441
+ expect(scroll_y).to be < 500
442
+ end
443
+
444
+ it 'should scroll the page with jquery animation', requires: [:js] do
445
+ @animation_session.visit('with_jquery_animation')
446
+ @animation_session.click_link('scroll top 500')
447
+ scroll_y = @animation_session.evaluate_script('window.scrollY')
448
+ expect(scroll_y).to be < 500
449
+ end
372
450
  end
373
451
 
374
452
  context 'if we pass in css that matches elements' do
@@ -382,12 +460,14 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
382
460
 
383
461
  it 'should disable CSS transitions' do
384
462
  @animation_session_with_matching_css.visit('with_animation')
463
+ sleep 1
385
464
  @animation_session_with_matching_css.click_link('transition me away')
386
465
  expect(@animation_session_with_matching_css).to have_no_link('transition me away', wait: 0.5)
387
466
  end
388
467
 
389
468
  it 'should disable CSS animations' do
390
469
  @animation_session_with_matching_css.visit('with_animation')
470
+ sleep 1
391
471
  @animation_session_with_matching_css.click_link('animate me away')
392
472
  expect(@animation_session_with_matching_css).to have_no_link('animate me away', wait: 0.5)
393
473
  end
@@ -404,6 +484,7 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
404
484
 
405
485
  it 'should not disable CSS transitions' do
406
486
  @animation_session_without_matching_css.visit('with_animation')
487
+ sleep 1
407
488
  @animation_session_without_matching_css.click_link('transition me away')
408
489
  sleep 0.5 # Wait long enough for click to have been processed
409
490
  expect(@animation_session_without_matching_css).to have_link('transition me away', wait: false)
@@ -412,6 +493,7 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
412
493
 
413
494
  it 'should not disable CSS animations' do
414
495
  @animation_session_without_matching_css.visit('with_animation')
496
+ sleep 1
415
497
  @animation_session_without_matching_css.click_link('animate me away')
416
498
  sleep 0.5 # Wait long enough for click to have been processed
417
499
  expect(@animation_session_without_matching_css).to have_link('animate me away', wait: false)
@@ -425,7 +507,7 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
425
507
  it 'can find html5 svg elements' do
426
508
  session.visit('with_html5_svg')
427
509
  expect(session).to have_selector(:element, :svg)
428
- expect(session).to have_selector(:element, :rect, visible: true)
510
+ expect(session).to have_selector(:element, :rect, visible: :visible)
429
511
  expect(session).to have_selector(:element, :circle)
430
512
  expect(session).to have_selector(:element, :linearGradient, visible: :all)
431
513
  end
@@ -441,21 +523,30 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
441
523
  context 'controlled components' do
442
524
  it 'can set and clear a text field' do
443
525
  skip "This test doesn't support older browsers" if ie?(session)
444
- # session.visit 'https://reactjs.org/docs/forms.html'
445
- # session.all(:css, 'h2#controlled-components ~ p a', text: 'Try it on CodePen')[0].click
446
- # copied into local view
526
+
447
527
  session.visit 'react'
448
- # Not necessary when accessed locally
449
- # session.within_frame(:css, 'iframe.result-iframe:not([src=""])', wait: 10) do
528
+
450
529
  session.fill_in('Name:', with: 'abc')
451
530
  session.accept_prompt 'A name was submitted: abc' do
452
531
  session.click_button('Submit')
453
532
  end
533
+
454
534
  session.fill_in('Name:', with: '')
455
535
  session.accept_prompt(/A name was submitted: $/) do
456
536
  session.click_button('Submit')
457
537
  end
458
- # end
538
+ end
539
+
540
+ it 'works with rapid fill' do
541
+ skip "This test doesn't support older browsers" if ie?(session)
542
+
543
+ session.visit 'react'
544
+ long_string = (0...60).map { |i| ((i % 26) + 65).chr }.join
545
+
546
+ session.fill_in('Name:', with: long_string)
547
+ session.accept_prompt "A name was submitted: #{long_string}" do
548
+ session.click_button('Submit')
549
+ end
459
550
  end
460
551
  end
461
552
  end