capybara 3.23.0 → 3.35.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|