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.
- checksums.yaml +4 -4
- data/History.md +264 -11
- data/README.md +10 -6
- data/lib/capybara.rb +20 -8
- data/lib/capybara/config.rb +10 -8
- data/lib/capybara/cucumber.rb +1 -1
- data/lib/capybara/driver/base.rb +4 -0
- data/lib/capybara/driver/node.rb +4 -0
- data/lib/capybara/dsl.rb +10 -2
- data/lib/capybara/helpers.rb +28 -2
- data/lib/capybara/minitest.rb +232 -144
- data/lib/capybara/minitest/spec.rb +156 -97
- data/lib/capybara/node/actions.rb +36 -36
- data/lib/capybara/node/base.rb +6 -6
- data/lib/capybara/node/document.rb +2 -2
- data/lib/capybara/node/document_matchers.rb +3 -3
- data/lib/capybara/node/element.rb +77 -33
- data/lib/capybara/node/finders.rb +24 -17
- data/lib/capybara/node/matchers.rb +79 -64
- data/lib/capybara/node/simple.rb +11 -4
- data/lib/capybara/queries/ancestor_query.rb +6 -10
- data/lib/capybara/queries/base_query.rb +2 -1
- data/lib/capybara/queries/current_path_query.rb +14 -4
- data/lib/capybara/queries/selector_query.rb +259 -23
- data/lib/capybara/queries/sibling_query.rb +5 -11
- data/lib/capybara/queries/style_query.rb +1 -1
- data/lib/capybara/queries/text_query.rb +13 -1
- data/lib/capybara/rack_test/browser.rb +13 -4
- data/lib/capybara/rack_test/driver.rb +2 -1
- data/lib/capybara/rack_test/form.rb +2 -2
- data/lib/capybara/rack_test/node.rb +42 -6
- data/lib/capybara/registration_container.rb +44 -0
- data/lib/capybara/registrations/drivers.rb +18 -12
- data/lib/capybara/registrations/patches/puma_ssl.rb +29 -0
- data/lib/capybara/registrations/servers.rb +9 -2
- data/lib/capybara/result.rb +39 -19
- data/lib/capybara/rspec.rb +2 -0
- data/lib/capybara/rspec/matcher_proxies.rb +5 -5
- data/lib/capybara/rspec/matchers.rb +97 -74
- data/lib/capybara/rspec/matchers/base.rb +19 -6
- data/lib/capybara/rspec/matchers/count_sugar.rb +2 -1
- data/lib/capybara/rspec/matchers/have_ancestor.rb +5 -7
- data/lib/capybara/rspec/matchers/have_current_path.rb +2 -2
- data/lib/capybara/rspec/matchers/have_selector.rb +15 -10
- data/lib/capybara/rspec/matchers/have_sibling.rb +4 -7
- data/lib/capybara/rspec/matchers/have_text.rb +4 -7
- data/lib/capybara/rspec/matchers/have_title.rb +2 -2
- data/lib/capybara/rspec/matchers/match_selector.rb +3 -3
- data/lib/capybara/rspec/matchers/match_style.rb +7 -2
- data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
- data/lib/capybara/selector.rb +46 -19
- data/lib/capybara/selector/builders/css_builder.rb +10 -6
- data/lib/capybara/selector/builders/xpath_builder.rb +4 -2
- data/lib/capybara/selector/css.rb +1 -1
- data/lib/capybara/selector/definition.rb +13 -11
- data/lib/capybara/selector/definition/button.rb +32 -15
- data/lib/capybara/selector/definition/checkbox.rb +2 -2
- data/lib/capybara/selector/definition/css.rb +3 -1
- data/lib/capybara/selector/definition/datalist_input.rb +2 -2
- data/lib/capybara/selector/definition/datalist_option.rb +1 -1
- data/lib/capybara/selector/definition/element.rb +3 -2
- data/lib/capybara/selector/definition/field.rb +1 -1
- data/lib/capybara/selector/definition/file_field.rb +1 -1
- data/lib/capybara/selector/definition/fillable_field.rb +2 -2
- data/lib/capybara/selector/definition/label.rb +5 -3
- data/lib/capybara/selector/definition/link.rb +8 -0
- data/lib/capybara/selector/definition/option.rb +1 -1
- data/lib/capybara/selector/definition/radio_button.rb +2 -2
- data/lib/capybara/selector/definition/select.rb +33 -14
- data/lib/capybara/selector/definition/table.rb +6 -3
- data/lib/capybara/selector/definition/table_row.rb +2 -2
- data/lib/capybara/selector/filter_set.rb +13 -11
- data/lib/capybara/selector/filters/base.rb +6 -1
- data/lib/capybara/selector/filters/locator_filter.rb +1 -1
- data/lib/capybara/selector/regexp_disassembler.rb +7 -0
- data/lib/capybara/selector/selector.rb +13 -3
- data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -1
- data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -1
- data/lib/capybara/selenium/atoms/src/getAttribute.js +1 -1
- data/lib/capybara/selenium/atoms/src/isDisplayed.js +10 -10
- data/lib/capybara/selenium/driver.rb +86 -24
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +24 -21
- data/lib/capybara/selenium/driver_specializations/edge_driver.rb +21 -19
- data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +17 -1
- data/lib/capybara/selenium/driver_specializations/safari_driver.rb +0 -4
- data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
- data/lib/capybara/selenium/extensions/find.rb +37 -26
- data/lib/capybara/selenium/extensions/html5_drag.rb +55 -11
- data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
- data/lib/capybara/selenium/extensions/scroll.rb +8 -10
- data/lib/capybara/selenium/logger_suppressor.rb +8 -2
- data/lib/capybara/selenium/node.rb +160 -40
- data/lib/capybara/selenium/nodes/chrome_node.rb +72 -12
- data/lib/capybara/selenium/nodes/edge_node.rb +32 -14
- data/lib/capybara/selenium/nodes/firefox_node.rb +28 -32
- data/lib/capybara/selenium/nodes/safari_node.rb +5 -29
- data/lib/capybara/selenium/patches/action_pauser.rb +26 -0
- data/lib/capybara/selenium/patches/atoms.rb +4 -4
- data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
- data/lib/capybara/selenium/patches/logs.rb +32 -7
- data/lib/capybara/server.rb +19 -3
- data/lib/capybara/server/animation_disabler.rb +8 -3
- data/lib/capybara/server/checker.rb +1 -1
- data/lib/capybara/server/middleware.rb +22 -10
- data/lib/capybara/session.rb +66 -40
- data/lib/capybara/session/config.rb +11 -3
- data/lib/capybara/session/matchers.rb +11 -11
- data/lib/capybara/spec/public/offset.js +6 -0
- data/lib/capybara/spec/public/test.js +75 -7
- data/lib/capybara/spec/session/accept_alert_spec.rb +1 -1
- data/lib/capybara/spec/session/all_spec.rb +60 -5
- data/lib/capybara/spec/session/ancestor_spec.rb +5 -0
- data/lib/capybara/spec/session/assert_text_spec.rb +9 -5
- data/lib/capybara/spec/session/check_spec.rb +6 -0
- data/lib/capybara/spec/session/click_button_spec.rb +16 -0
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +9 -0
- data/lib/capybara/spec/session/current_url_spec.rb +11 -1
- data/lib/capybara/spec/session/fill_in_spec.rb +29 -0
- data/lib/capybara/spec/session/find_spec.rb +55 -0
- data/lib/capybara/spec/session/has_ancestor_spec.rb +2 -0
- data/lib/capybara/spec/session/has_button_spec.rb +51 -0
- data/lib/capybara/spec/session/has_css_spec.rb +26 -4
- data/lib/capybara/spec/session/has_current_path_spec.rb +15 -2
- data/lib/capybara/spec/session/has_field_spec.rb +34 -0
- data/lib/capybara/spec/session/has_select_spec.rb +32 -4
- data/lib/capybara/spec/session/has_selector_spec.rb +4 -4
- data/lib/capybara/spec/session/has_table_spec.rb +51 -5
- data/lib/capybara/spec/session/has_text_spec.rb +30 -0
- data/lib/capybara/spec/session/html_spec.rb +1 -1
- data/lib/capybara/spec/session/matches_style_spec.rb +2 -2
- data/lib/capybara/spec/session/node_spec.rb +394 -9
- data/lib/capybara/spec/session/refresh_spec.rb +2 -1
- data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +2 -2
- data/lib/capybara/spec/session/save_page_spec.rb +4 -4
- data/lib/capybara/spec/session/save_screenshot_spec.rb +4 -15
- data/lib/capybara/spec/session/selectors_spec.rb +16 -3
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +1 -1
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +1 -1
- data/lib/capybara/spec/session/window/window_spec.rb +8 -8
- data/lib/capybara/spec/session/window/windows_spec.rb +1 -1
- data/lib/capybara/spec/spec_helper.rb +14 -14
- data/lib/capybara/spec/test_app.rb +27 -21
- data/lib/capybara/spec/views/form.erb +47 -4
- data/lib/capybara/spec/views/offset.erb +32 -0
- data/lib/capybara/spec/views/spatial.erb +31 -0
- data/lib/capybara/spec/views/with_animation.erb +37 -1
- data/lib/capybara/spec/views/with_dragula.erb +24 -0
- data/lib/capybara/spec/views/with_html.erb +24 -2
- data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
- data/lib/capybara/spec/views/with_js.erb +4 -1
- data/lib/capybara/spec/views/with_jstree.erb +26 -0
- data/lib/capybara/spec/views/with_sortable_js.erb +1 -1
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/window.rb +3 -7
- data/spec/basic_node_spec.rb +15 -14
- data/spec/capybara_spec.rb +28 -28
- data/spec/dsl_spec.rb +16 -3
- data/spec/filter_set_spec.rb +5 -5
- data/spec/fixtures/selenium_driver_rspec_failure.rb +1 -1
- data/spec/fixtures/selenium_driver_rspec_success.rb +1 -1
- data/spec/minitest_spec.rb +3 -2
- data/spec/minitest_spec_spec.rb +46 -46
- data/spec/rack_test_spec.rb +38 -15
- data/spec/regexp_dissassembler_spec.rb +52 -38
- data/spec/result_spec.rb +43 -32
- data/spec/rspec/features_spec.rb +4 -1
- data/spec/rspec/scenarios_spec.rb +4 -0
- data/spec/rspec/shared_spec_matchers.rb +68 -56
- data/spec/rspec_spec.rb +9 -5
- data/spec/selector_spec.rb +32 -17
- data/spec/selenium_spec_chrome.rb +78 -11
- data/spec/selenium_spec_chrome_remote.rb +23 -6
- data/spec/selenium_spec_edge.rb +15 -12
- data/spec/selenium_spec_firefox.rb +24 -19
- data/spec/selenium_spec_firefox_remote.rb +0 -8
- data/spec/selenium_spec_ie.rb +1 -6
- data/spec/server_spec.rb +106 -44
- data/spec/session_spec.rb +5 -5
- data/spec/shared_selenium_node.rb +56 -2
- data/spec/shared_selenium_session.rb +122 -15
- data/spec/spec_helper.rb +2 -2
- metadata +63 -17
- data/lib/capybara/spec/session/source_spec.rb +0 -0
@@ -50,12 +50,6 @@ module TestSessions
|
|
50
50
|
RemoteFirefox = Capybara::Session.new(FIREFOX_REMOTE_DRIVER, TestApp)
|
51
51
|
end
|
52
52
|
|
53
|
-
TestSessions::RemoteFirefox.driver.browser.file_detector = lambda do |args|
|
54
|
-
# args => ["/path/to/file"]
|
55
|
-
str = args.first.to_s
|
56
|
-
str if File.exist?(str)
|
57
|
-
end
|
58
|
-
|
59
53
|
skipped_tests = %i[response_headers status_code trigger download]
|
60
54
|
|
61
55
|
Capybara::SpecHelper.run_specs TestSessions::RemoteFirefox, FIREFOX_REMOTE_DRIVER.to_s, capybara_skip: skipped_tests do |example|
|
@@ -71,8 +65,6 @@ Capybara::SpecHelper.run_specs TestSessions::RemoteFirefox, FIREFOX_REMOTE_DRIVE
|
|
71
65
|
when /#accept_confirm should work with nested modals$/
|
72
66
|
# skip because this is timing based and hence flaky when set to pending
|
73
67
|
skip 'Broken in FF 63 - https://bugzilla.mozilla.org/show_bug.cgi?id=1487358' if firefox_gte?(63, @session)
|
74
|
-
when 'Capybara::Session selenium_firefox_remote #attach_file with a block can upload by clicking the file input'
|
75
|
-
pending "Geckodriver doesn't allow clicking on file inputs"
|
76
68
|
end
|
77
69
|
end
|
78
70
|
|
data/spec/selenium_spec_ie.rb
CHANGED
@@ -39,12 +39,7 @@ Capybara.register_driver :selenium_ie do |app|
|
|
39
39
|
Capybara::Selenium::Driver.new(app,
|
40
40
|
browser: :remote,
|
41
41
|
options: options,
|
42
|
-
url: url)
|
43
|
-
driver.browser.file_detector = lambda do |args|
|
44
|
-
str = args.first.to_s
|
45
|
-
str if File.exist?(str)
|
46
|
-
end
|
47
|
-
end
|
42
|
+
url: url)
|
48
43
|
else
|
49
44
|
Capybara::Selenium::Driver.new(
|
50
45
|
app,
|
data/spec/server_spec.rb
CHANGED
@@ -5,7 +5,7 @@ require 'spec_helper'
|
|
5
5
|
RSpec.describe Capybara::Server do
|
6
6
|
it 'should spool up a rack server' do
|
7
7
|
app = proc { |_env| [200, {}, ['Hello Server!']] }
|
8
|
-
server =
|
8
|
+
server = described_class.new(app).boot
|
9
9
|
|
10
10
|
res = Net::HTTP.start(server.host, server.port) { |http| http.get('/') }
|
11
11
|
|
@@ -14,7 +14,7 @@ RSpec.describe Capybara::Server do
|
|
14
14
|
|
15
15
|
it 'should do nothing when no server given' do
|
16
16
|
expect do
|
17
|
-
|
17
|
+
described_class.new(nil).boot
|
18
18
|
end.not_to raise_error
|
19
19
|
end
|
20
20
|
|
@@ -26,12 +26,12 @@ RSpec.describe Capybara::Server do
|
|
26
26
|
app = proc { |_env| [200, {}, ['Hello Server!']] }
|
27
27
|
|
28
28
|
Capybara.server_host = '127.0.0.1'
|
29
|
-
server =
|
29
|
+
server = described_class.new(app).boot
|
30
30
|
res = Net::HTTP.get(URI("http://127.0.0.1:#{server.port}"))
|
31
31
|
expect(res).to eq('Hello Server!')
|
32
32
|
|
33
33
|
Capybara.server_host = '0.0.0.0'
|
34
|
-
server =
|
34
|
+
server = described_class.new(app).boot
|
35
35
|
res = Net::HTTP.get(URI("http://127.0.0.1:#{server.port}"))
|
36
36
|
expect(res).to eq('Hello Server!')
|
37
37
|
ensure
|
@@ -43,7 +43,7 @@ RSpec.describe Capybara::Server do
|
|
43
43
|
Capybara.server_port = 22789
|
44
44
|
|
45
45
|
app = proc { |_env| [200, {}, ['Hello Server!']] }
|
46
|
-
server =
|
46
|
+
server = described_class.new(app).boot
|
47
47
|
|
48
48
|
res = Net::HTTP.start(server.host, 22789) { |http| http.get('/') }
|
49
49
|
expect(res.body).to include('Hello Server')
|
@@ -53,7 +53,7 @@ RSpec.describe Capybara::Server do
|
|
53
53
|
|
54
54
|
it 'should use given port' do
|
55
55
|
app = proc { |_env| [200, {}, ['Hello Server!']] }
|
56
|
-
server =
|
56
|
+
server = described_class.new(app, port: 22790).boot
|
57
57
|
|
58
58
|
res = Net::HTTP.start(server.host, 22790) { |http| http.get('/') }
|
59
59
|
expect(res.body).to include('Hello Server')
|
@@ -66,7 +66,7 @@ RSpec.describe Capybara::Server do
|
|
66
66
|
apps = responses.map do |response|
|
67
67
|
proc { |_env| [200, {}, [response]] }
|
68
68
|
end
|
69
|
-
servers = apps.map { |app|
|
69
|
+
servers = apps.map { |app| described_class.new(app).boot }
|
70
70
|
|
71
71
|
servers.each_with_index do |server, idx|
|
72
72
|
result = Net::HTTP.start(server.host, server.port) { |http| http.get('/') }
|
@@ -74,28 +74,72 @@ RSpec.describe Capybara::Server do
|
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
77
|
-
it 'should
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
77
|
+
it 'should handle that getting available ports fails randomly' do
|
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)
|
85
|
+
ensure
|
86
|
+
call_count += 1
|
87
|
+
end
|
84
88
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
93
|
+
end
|
90
94
|
|
91
|
-
|
92
|
-
|
93
|
-
|
95
|
+
it 'should return its #base_url' do
|
96
|
+
app = proc { |_env| [200, {}, ['Hello Server!']] }
|
97
|
+
server = described_class.new(app).boot
|
98
|
+
uri = ::Addressable::URI.parse(server.base_url)
|
99
|
+
expect(uri.to_hash).to include(scheme: 'http', host: server.host, port: server.port)
|
100
|
+
end
|
94
101
|
|
95
|
-
|
96
|
-
|
97
|
-
|
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
|
120
|
+
|
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
|
127
|
+
|
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)
|
133
|
+
|
134
|
+
res = Net::HTTP.start(server.host, server.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) do |https|
|
135
|
+
https.get('/')
|
98
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
|
99
143
|
end
|
100
144
|
|
101
145
|
context 'When Capybara.reuse_server is true' do
|
@@ -112,7 +156,7 @@ RSpec.describe Capybara::Server do
|
|
112
156
|
it 'should use the existing server if it already running' do
|
113
157
|
app = proc { |_env| [200, {}, ['Hello Server!']] }
|
114
158
|
|
115
|
-
servers = Array.new(2) {
|
159
|
+
servers = Array.new(2) { described_class.new(app).boot }
|
116
160
|
|
117
161
|
servers.each do |server|
|
118
162
|
res = Net::HTTP.start(server.host, server.port) { |http| http.get('/') }
|
@@ -132,8 +176,8 @@ RSpec.describe Capybara::Server do
|
|
132
176
|
[200, {}, ['Hello Server!']]
|
133
177
|
end
|
134
178
|
|
135
|
-
server1 =
|
136
|
-
server2 =
|
179
|
+
server1 = described_class.new(app).boot
|
180
|
+
server2 = described_class.new(app).boot
|
137
181
|
|
138
182
|
expect do
|
139
183
|
start_request(server1, 1.0)
|
@@ -157,7 +201,7 @@ RSpec.describe Capybara::Server do
|
|
157
201
|
it 'should not reuse an already running server' do
|
158
202
|
app = proc { |_env| [200, {}, ['Hello Server!']] }
|
159
203
|
|
160
|
-
servers = Array.new(2) {
|
204
|
+
servers = Array.new(2) { described_class.new(app).boot }
|
161
205
|
|
162
206
|
servers.each do |server|
|
163
207
|
res = Net::HTTP.start(server.host, server.port) { |http| http.get('/') }
|
@@ -177,8 +221,8 @@ RSpec.describe Capybara::Server do
|
|
177
221
|
[200, {}, ['Hello Server!']]
|
178
222
|
end
|
179
223
|
|
180
|
-
server1 =
|
181
|
-
server2 =
|
224
|
+
server1 = described_class.new(app).boot
|
225
|
+
server2 = described_class.new(app).boot
|
182
226
|
|
183
227
|
expect do
|
184
228
|
start_request(server1, 1.0)
|
@@ -193,24 +237,42 @@ RSpec.describe Capybara::Server do
|
|
193
237
|
end
|
194
238
|
|
195
239
|
it 'should raise server errors when the server errors before the timeout' do
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
Capybara.server = :kaboom
|
240
|
+
Capybara.register_server :kaboom do
|
241
|
+
sleep 0.1
|
242
|
+
raise 'kaboom'
|
243
|
+
end
|
244
|
+
Capybara.server = :kaboom
|
202
245
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
246
|
+
expect do
|
247
|
+
described_class.new(proc { |e| }).boot
|
248
|
+
end.to raise_error(RuntimeError, 'kaboom')
|
249
|
+
ensure
|
250
|
+
Capybara.server = :default
|
251
|
+
end
|
252
|
+
|
253
|
+
it 'should raise an error when there are pending requests' do
|
254
|
+
app = proc do |env|
|
255
|
+
request = Rack::Request.new(env)
|
256
|
+
sleep request.params['wait_time'].to_f
|
257
|
+
[200, {}, ['Hello Server!']]
|
208
258
|
end
|
259
|
+
|
260
|
+
server = described_class.new(app).boot
|
261
|
+
|
262
|
+
expect do
|
263
|
+
start_request(server, 59.0)
|
264
|
+
server.wait_for_pending_requests
|
265
|
+
end.not_to raise_error
|
266
|
+
|
267
|
+
expect do
|
268
|
+
start_request(server, 61.0)
|
269
|
+
server.wait_for_pending_requests
|
270
|
+
end.to raise_error('Requests did not finish in 60 seconds: ["/?wait_time=61.0"]')
|
209
271
|
end
|
210
272
|
|
211
273
|
it 'is not #responsive? when Net::HTTP raises a SystemCallError' do
|
212
274
|
app = -> { [200, {}, ['Hello, world']] }
|
213
|
-
server =
|
275
|
+
server = described_class.new(app)
|
214
276
|
allow(Net::HTTP).to receive(:start).and_raise(SystemCallError.allocate)
|
215
277
|
expect(server.responsive?).to eq false
|
216
278
|
end
|
@@ -225,7 +287,7 @@ RSpec.describe Capybara::Server do
|
|
225
287
|
response = Net::HTTPSuccess.allocate
|
226
288
|
allow(response).to receive(:body).and_return app.object_id.to_s
|
227
289
|
allow(Net::HTTP).to receive(:start).with(anything, anything, hash_including(use_ssl: true)).and_return(response).once
|
228
|
-
|
290
|
+
described_class.new(app).boot
|
229
291
|
expect(Net::HTTP).to have_received(:start).exactly(3).times
|
230
292
|
end
|
231
293
|
end
|
data/spec/session_spec.rb
CHANGED
@@ -3,16 +3,16 @@
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
RSpec.describe Capybara::Session do
|
6
|
-
|
6
|
+
describe '#new' do
|
7
7
|
it 'should raise an error if passed non-existent driver' do
|
8
8
|
expect do
|
9
|
-
|
9
|
+
described_class.new(:quox, TestApp).driver
|
10
10
|
end.to raise_error(Capybara::DriverNotFoundError)
|
11
11
|
end
|
12
12
|
|
13
13
|
it 'verifies a passed app is a rack app' do
|
14
14
|
expect do
|
15
|
-
|
15
|
+
described_class.new(:unknown, random: 'hash')
|
16
16
|
end.to raise_error TypeError, 'The second parameter to Session::new should be a rack app if passed.'
|
17
17
|
end
|
18
18
|
end
|
@@ -75,14 +75,14 @@ RSpec.describe Capybara::Session do
|
|
75
75
|
|
76
76
|
context 'quit' do
|
77
77
|
it 'will reset the driver' do
|
78
|
-
session =
|
78
|
+
session = described_class.new(:rack_test, TestApp)
|
79
79
|
driver = session.driver
|
80
80
|
session.quit
|
81
81
|
expect(session.driver).not_to eql driver
|
82
82
|
end
|
83
83
|
|
84
84
|
it 'resets the document' do
|
85
|
-
session =
|
85
|
+
session = described_class.new(:rack_test, TestApp)
|
86
86
|
document = session.document
|
87
87
|
session.quit
|
88
88
|
expect(session.document.base).not_to eql document.base
|
@@ -6,7 +6,7 @@ require 'selenium-webdriver'
|
|
6
6
|
RSpec.shared_examples 'Capybara::Node' do |session, _mode|
|
7
7
|
let(:session) { session }
|
8
8
|
|
9
|
-
|
9
|
+
describe '#content_editable?' do
|
10
10
|
it 'returns true when the element is content editable' do
|
11
11
|
session.visit('/with_js')
|
12
12
|
expect(session.find(:css, '#existing_content_editable').base.content_editable?).to be true
|
@@ -19,11 +19,65 @@ RSpec.shared_examples 'Capybara::Node' do |session, _mode|
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
|
22
|
+
describe '#send_keys' do
|
23
23
|
it 'should process space' do
|
24
24
|
session.visit('/form')
|
25
25
|
session.find(:css, '#address1_city').send_keys('ocean', [:shift, :space, 'side'])
|
26
26
|
expect(session.find(:css, '#address1_city').value).to eq 'ocean SIDE'
|
27
27
|
end
|
28
28
|
end
|
29
|
+
|
30
|
+
describe '#[]' do
|
31
|
+
it 'should work for spellcheck' do
|
32
|
+
session.visit('/with_html')
|
33
|
+
expect(session.find('//input[@spellcheck="TRUE"]')[:spellcheck]).to eq('true')
|
34
|
+
expect(session.find('//input[@spellcheck="FALSE"]')[:spellcheck]).to eq('false')
|
35
|
+
end
|
36
|
+
end
|
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
|
+
|
48
|
+
describe '#visible?' do
|
49
|
+
let(:bridge) do
|
50
|
+
session.driver.browser.send(:bridge)
|
51
|
+
end
|
52
|
+
|
53
|
+
around do |example|
|
54
|
+
native_displayed = session.driver.options[:native_displayed]
|
55
|
+
example.run
|
56
|
+
session.driver.options[:native_displayed] = native_displayed
|
57
|
+
end
|
58
|
+
|
59
|
+
before do
|
60
|
+
allow(bridge).to receive(:execute_atom).and_call_original
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'will use native displayed if told to' do
|
64
|
+
pending "Chromedriver < 76.0.3809.25 doesn't support native displayed in W3C mode" if chrome_lt?(76, session) && (ENV['W3C'] != 'false')
|
65
|
+
|
66
|
+
session.driver.options[:native_displayed] = true
|
67
|
+
session.visit('/form')
|
68
|
+
session.find(:css, '#address1_city', visible: true)
|
69
|
+
|
70
|
+
expect(bridge).not_to have_received(:execute_atom)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "won't use native displayed if told not to" do
|
74
|
+
skip 'Non-W3C uses native' if chrome?(session) && (ENV['W3C'] == 'false')
|
75
|
+
|
76
|
+
session.driver.options[:native_displayed] = false
|
77
|
+
session.visit('/form')
|
78
|
+
session.find(:css, '#address1_city', visible: true)
|
79
|
+
|
80
|
+
expect(bridge).to have_received(:execute_atom).with(:isDisplayed, any_args)
|
81
|
+
end
|
82
|
+
end
|
29
83
|
end
|
@@ -72,7 +72,7 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
|
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
-
|
75
|
+
describe '#fill_in_with empty string and no options' do
|
76
76
|
it 'should trigger change when clearing a field' do
|
77
77
|
pending "safaridriver doesn't trigger change for clear" if safari?(session)
|
78
78
|
session.visit('/with_js')
|
@@ -83,7 +83,7 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
|
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
86
|
-
|
86
|
+
describe '#fill_in with { :clear => :backspace } fill_option', requires: [:js] do
|
87
87
|
before do
|
88
88
|
# Firefox has an issue with change events if the main window doesn't think it's focused
|
89
89
|
session.execute_script('window.focus()')
|
@@ -150,7 +150,7 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
|
|
150
150
|
end
|
151
151
|
end
|
152
152
|
|
153
|
-
|
153
|
+
describe '#fill_in with { clear: :none } fill_options' do
|
154
154
|
it 'should append to content in a field' do
|
155
155
|
pending 'Safari overwrites by default - need to figure out a workaround' if safari?(session)
|
156
156
|
|
@@ -160,9 +160,18 @@ 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
|
166
175
|
before do
|
167
176
|
session.visit('/form')
|
168
177
|
session.find(:css, '#form_date').execute_script <<-JS
|
@@ -194,7 +203,7 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
|
|
194
203
|
end
|
195
204
|
end
|
196
205
|
|
197
|
-
|
206
|
+
describe '#fill_in with { clear: Array } fill_options' do
|
198
207
|
it 'should pass the array through to the element' do
|
199
208
|
# this is mainly for use with [[:control, 'a'], :backspace] - however since that is platform dependant I'm testing with something less useful
|
200
209
|
session.visit('/form')
|
@@ -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
|
@@ -299,8 +328,9 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
|
|
299
328
|
describe 'Element#click' do
|
300
329
|
it 'should handle fixed headers/footers' do
|
301
330
|
session.visit('/with_fixed_header_footer')
|
302
|
-
|
303
|
-
|
331
|
+
session.using_wait_time(2) do
|
332
|
+
session.find(:link, 'Go to root').click
|
333
|
+
end
|
304
334
|
expect(session).to have_current_path('/')
|
305
335
|
end
|
306
336
|
end
|
@@ -321,6 +351,15 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
|
|
321
351
|
session.click_button('Upload Multiple')
|
322
352
|
expect(session.body).to include('5 | ') # number of files
|
323
353
|
end
|
354
|
+
|
355
|
+
it 'can attach a relative file' do
|
356
|
+
pending 'Geckdoriver on windows requires alternate file separator which path expansion replaces' if Gem.win_platform? && firefox?(session)
|
357
|
+
|
358
|
+
session.visit('/form')
|
359
|
+
session.attach_file('Single Document', 'spec/fixtures/capybara.csv')
|
360
|
+
session.click_button('Upload Single')
|
361
|
+
expect(session.body).to include('Content-type: text/csv')
|
362
|
+
end
|
324
363
|
end
|
325
364
|
|
326
365
|
context 'Windows' do
|
@@ -348,11 +387,66 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
|
|
348
387
|
expect(@animation_session).to have_no_link('transition me away', wait: 0.5)
|
349
388
|
end
|
350
389
|
|
351
|
-
it 'should disable CSS animations' do
|
390
|
+
it 'should disable CSS animations (set to 0s)' do
|
352
391
|
@animation_session.visit('with_animation')
|
392
|
+
sleep 1
|
353
393
|
@animation_session.click_link('animate me away')
|
354
394
|
expect(@animation_session).to have_no_link('animate me away', wait: 0.5)
|
355
395
|
end
|
396
|
+
|
397
|
+
it 'should disable CSS animations on pseudo elements (set to 0s)' do
|
398
|
+
@animation_session.visit('with_animation')
|
399
|
+
sleep 1
|
400
|
+
@animation_session.find_link('animate me away').right_click
|
401
|
+
expect(@animation_session).to have_content('Animation Ended', wait: 0.1)
|
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
|
356
450
|
end
|
357
451
|
|
358
452
|
context 'if we pass in css that matches elements' do
|
@@ -366,12 +460,14 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
|
|
366
460
|
|
367
461
|
it 'should disable CSS transitions' do
|
368
462
|
@animation_session_with_matching_css.visit('with_animation')
|
463
|
+
sleep 1
|
369
464
|
@animation_session_with_matching_css.click_link('transition me away')
|
370
465
|
expect(@animation_session_with_matching_css).to have_no_link('transition me away', wait: 0.5)
|
371
466
|
end
|
372
467
|
|
373
468
|
it 'should disable CSS animations' do
|
374
469
|
@animation_session_with_matching_css.visit('with_animation')
|
470
|
+
sleep 1
|
375
471
|
@animation_session_with_matching_css.click_link('animate me away')
|
376
472
|
expect(@animation_session_with_matching_css).to have_no_link('animate me away', wait: 0.5)
|
377
473
|
end
|
@@ -388,6 +484,7 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
|
|
388
484
|
|
389
485
|
it 'should not disable CSS transitions' do
|
390
486
|
@animation_session_without_matching_css.visit('with_animation')
|
487
|
+
sleep 1
|
391
488
|
@animation_session_without_matching_css.click_link('transition me away')
|
392
489
|
sleep 0.5 # Wait long enough for click to have been processed
|
393
490
|
expect(@animation_session_without_matching_css).to have_link('transition me away', wait: false)
|
@@ -396,6 +493,7 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
|
|
396
493
|
|
397
494
|
it 'should not disable CSS animations' do
|
398
495
|
@animation_session_without_matching_css.visit('with_animation')
|
496
|
+
sleep 1
|
399
497
|
@animation_session_without_matching_css.click_link('animate me away')
|
400
498
|
sleep 0.5 # Wait long enough for click to have been processed
|
401
499
|
expect(@animation_session_without_matching_css).to have_link('animate me away', wait: false)
|
@@ -409,7 +507,7 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
|
|
409
507
|
it 'can find html5 svg elements' do
|
410
508
|
session.visit('with_html5_svg')
|
411
509
|
expect(session).to have_selector(:element, :svg)
|
412
|
-
expect(session).to have_selector(:element, :rect, visible:
|
510
|
+
expect(session).to have_selector(:element, :rect, visible: :visible)
|
413
511
|
expect(session).to have_selector(:element, :circle)
|
414
512
|
expect(session).to have_selector(:element, :linearGradient, visible: :all)
|
415
513
|
end
|
@@ -425,21 +523,30 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
|
|
425
523
|
context 'controlled components' do
|
426
524
|
it 'can set and clear a text field' do
|
427
525
|
skip "This test doesn't support older browsers" if ie?(session)
|
428
|
-
|
429
|
-
# session.all(:css, 'h2#controlled-components ~ p a', text: 'Try it on CodePen')[0].click
|
430
|
-
# copied into local view
|
526
|
+
|
431
527
|
session.visit 'react'
|
432
|
-
|
433
|
-
# session.within_frame(:css, 'iframe.result-iframe:not([src=""])', wait: 10) do
|
528
|
+
|
434
529
|
session.fill_in('Name:', with: 'abc')
|
435
530
|
session.accept_prompt 'A name was submitted: abc' do
|
436
531
|
session.click_button('Submit')
|
437
532
|
end
|
533
|
+
|
438
534
|
session.fill_in('Name:', with: '')
|
439
535
|
session.accept_prompt(/A name was submitted: $/) do
|
440
536
|
session.click_button('Submit')
|
441
537
|
end
|
442
|
-
|
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
|
443
550
|
end
|
444
551
|
end
|
445
552
|
end
|