capybara 3.35.0 → 3.40.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.
- checksums.yaml +4 -4
- data/History.md +168 -5
- data/README.md +199 -39
- data/lib/capybara/config.rb +16 -4
- data/lib/capybara/driver/base.rb +4 -0
- data/lib/capybara/driver/node.rb +5 -1
- data/lib/capybara/dsl.rb +4 -10
- data/lib/capybara/helpers.rb +9 -14
- data/lib/capybara/minitest/spec.rb +18 -6
- data/lib/capybara/minitest.rb +14 -1
- data/lib/capybara/node/actions.rb +14 -9
- data/lib/capybara/node/base.rb +2 -1
- data/lib/capybara/node/document.rb +2 -2
- data/lib/capybara/node/element.rb +13 -2
- data/lib/capybara/node/finders.rb +11 -2
- data/lib/capybara/node/matchers.rb +25 -0
- data/lib/capybara/node/simple.rb +5 -1
- data/lib/capybara/node/whitespace_normalizer.rb +81 -0
- data/lib/capybara/queries/active_element_query.rb +18 -0
- data/lib/capybara/queries/ancestor_query.rb +2 -1
- data/lib/capybara/queries/base_query.rb +2 -2
- data/lib/capybara/queries/current_path_query.rb +1 -1
- data/lib/capybara/queries/selector_query.rb +40 -11
- data/lib/capybara/queries/sibling_query.rb +2 -1
- data/lib/capybara/queries/text_query.rb +1 -1
- data/lib/capybara/rack_test/browser.rb +64 -8
- data/lib/capybara/rack_test/driver.rb +4 -4
- data/lib/capybara/rack_test/form.rb +29 -7
- data/lib/capybara/rack_test/node.rb +32 -33
- data/lib/capybara/registration_container.rb +2 -5
- data/lib/capybara/registrations/drivers.rb +7 -7
- data/lib/capybara/registrations/servers.rb +37 -16
- data/lib/capybara/result.rb +2 -2
- data/lib/capybara/rspec/matcher_proxies.rb +6 -6
- data/lib/capybara/rspec/matchers/base.rb +8 -6
- data/lib/capybara/rspec/matchers/compound.rb +1 -1
- data/lib/capybara/rspec/matchers/have_selector.rb +9 -17
- data/lib/capybara/rspec/matchers.rb +21 -16
- data/lib/capybara/selector/builders/css_builder.rb +1 -1
- data/lib/capybara/selector/builders/xpath_builder.rb +1 -1
- data/lib/capybara/selector/css.rb +6 -6
- data/lib/capybara/selector/definition/button.rb +10 -5
- data/lib/capybara/selector/definition/checkbox.rb +1 -1
- data/lib/capybara/selector/definition/file_field.rb +1 -1
- data/lib/capybara/selector/definition/fillable_field.rb +1 -1
- data/lib/capybara/selector/definition/link.rb +2 -1
- data/lib/capybara/selector/definition/radio_button.rb +1 -1
- data/lib/capybara/selector/definition/table.rb +1 -1
- data/lib/capybara/selector/definition/table_row.rb +2 -2
- data/lib/capybara/selector/definition.rb +4 -2
- data/lib/capybara/selector/filter_set.rb +4 -7
- data/lib/capybara/selector/regexp_disassembler.rb +2 -5
- data/lib/capybara/selector/selector.rb +5 -1
- data/lib/capybara/selector.rb +252 -0
- data/lib/capybara/selenium/driver.rb +31 -54
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +1 -1
- data/lib/capybara/selenium/driver_specializations/edge_driver.rb +9 -5
- data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +2 -7
- data/lib/capybara/selenium/extensions/html5_drag.rb +5 -4
- data/lib/capybara/selenium/node.rb +60 -38
- data/lib/capybara/selenium/nodes/chrome_node.rb +4 -16
- data/lib/capybara/selenium/nodes/edge_node.rb +19 -13
- data/lib/capybara/selenium/nodes/firefox_node.rb +3 -3
- data/lib/capybara/selenium/nodes/safari_node.rb +4 -4
- data/lib/capybara/selenium/patches/atoms.rb +1 -1
- data/lib/capybara/selenium/patches/pause_duration_fix.rb +1 -1
- data/lib/capybara/server/animation_disabler.rb +40 -23
- data/lib/capybara/server/middleware.rb +1 -1
- data/lib/capybara/server.rb +1 -1
- data/lib/capybara/session/config.rb +4 -2
- data/lib/capybara/session.rb +34 -34
- data/lib/capybara/spec/public/test.js +4 -0
- data/lib/capybara/spec/session/active_element_spec.rb +31 -0
- data/lib/capybara/spec/session/all_spec.rb +11 -15
- data/lib/capybara/spec/session/assert_text_spec.rb +17 -17
- data/lib/capybara/spec/session/attach_file_spec.rb +6 -0
- data/lib/capybara/spec/session/check_spec.rb +10 -0
- data/lib/capybara/spec/session/choose_spec.rb +6 -0
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +9 -0
- data/lib/capybara/spec/session/click_link_spec.rb +12 -1
- data/lib/capybara/spec/session/current_scope_spec.rb +1 -1
- data/lib/capybara/spec/session/fill_in_spec.rb +6 -0
- data/lib/capybara/spec/session/find_link_spec.rb +10 -0
- data/lib/capybara/spec/session/find_spec.rb +15 -1
- data/lib/capybara/spec/session/first_spec.rb +1 -1
- data/lib/capybara/spec/session/frame/within_frame_spec.rb +2 -0
- data/lib/capybara/spec/session/has_all_selectors_spec.rb +5 -5
- data/lib/capybara/spec/session/has_ancestor_spec.rb +2 -2
- data/lib/capybara/spec/session/has_any_selectors_spec.rb +6 -2
- data/lib/capybara/spec/session/has_button_spec.rb +30 -0
- data/lib/capybara/spec/session/has_current_path_spec.rb +3 -3
- data/lib/capybara/spec/session/has_element_spec.rb +47 -0
- data/lib/capybara/spec/session/has_field_spec.rb +25 -1
- data/lib/capybara/spec/session/has_link_spec.rb +40 -0
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +7 -7
- data/lib/capybara/spec/session/has_select_spec.rb +10 -4
- data/lib/capybara/spec/session/has_selector_spec.rb +15 -0
- data/lib/capybara/spec/session/has_table_spec.rb +13 -2
- data/lib/capybara/spec/session/has_text_spec.rb +6 -14
- data/lib/capybara/spec/session/matches_style_spec.rb +2 -0
- data/lib/capybara/spec/session/node_spec.rb +88 -1
- data/lib/capybara/spec/session/node_wrapper_spec.rb +1 -1
- data/lib/capybara/spec/session/reset_session_spec.rb +13 -0
- data/lib/capybara/spec/session/scroll_spec.rb +7 -5
- data/lib/capybara/spec/session/uncheck_spec.rb +1 -1
- data/lib/capybara/spec/session/visit_spec.rb +20 -0
- data/lib/capybara/spec/session/window/window_spec.rb +1 -1
- data/lib/capybara/spec/session/window/windows_spec.rb +1 -1
- data/lib/capybara/spec/session/within_spec.rb +13 -0
- data/lib/capybara/spec/spec_helper.rb +12 -5
- data/lib/capybara/spec/test_app.rb +91 -14
- data/lib/capybara/spec/views/animated.erb +1 -1
- data/lib/capybara/spec/views/form.erb +34 -4
- data/lib/capybara/spec/views/frame_child.erb +1 -1
- data/lib/capybara/spec/views/frame_one.erb +1 -1
- data/lib/capybara/spec/views/frame_parent.erb +1 -1
- data/lib/capybara/spec/views/frame_two.erb +1 -1
- data/lib/capybara/spec/views/initial_alert.erb +2 -1
- data/lib/capybara/spec/views/layout.erb +10 -0
- data/lib/capybara/spec/views/obscured.erb +1 -1
- data/lib/capybara/spec/views/offset.erb +2 -1
- data/lib/capybara/spec/views/path.erb +2 -2
- data/lib/capybara/spec/views/popup_one.erb +1 -1
- data/lib/capybara/spec/views/popup_two.erb +1 -1
- data/lib/capybara/spec/views/react.erb +2 -2
- data/lib/capybara/spec/views/scroll.erb +2 -1
- data/lib/capybara/spec/views/spatial.erb +1 -1
- data/lib/capybara/spec/views/with_animation.erb +2 -3
- data/lib/capybara/spec/views/with_base_tag.erb +2 -2
- data/lib/capybara/spec/views/with_dragula.erb +2 -2
- data/lib/capybara/spec/views/with_fixed_header_footer.erb +2 -1
- data/lib/capybara/spec/views/with_hover.erb +2 -2
- data/lib/capybara/spec/views/with_html.erb +5 -3
- data/lib/capybara/spec/views/with_jquery_animation.erb +1 -1
- data/lib/capybara/spec/views/with_js.erb +2 -3
- data/lib/capybara/spec/views/with_jstree.erb +1 -1
- data/lib/capybara/spec/views/with_namespace.erb +1 -0
- data/lib/capybara/spec/views/with_scope.erb +2 -2
- data/lib/capybara/spec/views/with_shadow.erb +31 -0
- data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
- data/lib/capybara/spec/views/with_sortable_js.erb +2 -2
- data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
- data/lib/capybara/spec/views/with_windows.erb +1 -1
- data/lib/capybara/spec/views/within_frames.erb +1 -1
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/window.rb +1 -1
- data/lib/capybara.rb +30 -30
- data/spec/basic_node_spec.rb +16 -3
- data/spec/capybara_spec.rb +12 -0
- data/spec/counter_spec.rb +35 -0
- data/spec/css_builder_spec.rb +1 -1
- data/spec/css_splitter_spec.rb +1 -1
- data/spec/dsl_spec.rb +5 -3
- data/spec/fixtures/selenium_driver_rspec_failure.rb +2 -2
- data/spec/fixtures/selenium_driver_rspec_success.rb +2 -2
- data/spec/minitest_spec.rb +12 -1
- data/spec/minitest_spec_spec.rb +4 -0
- data/spec/per_session_config_spec.rb +1 -1
- data/spec/rack_test_spec.rb +30 -12
- data/spec/result_spec.rb +41 -35
- data/spec/rspec/features_spec.rb +3 -3
- data/spec/rspec/scenarios_spec.rb +2 -2
- data/spec/rspec/shared_spec_matchers.rb +27 -3
- data/spec/rspec_matchers_spec.rb +25 -0
- data/spec/rspec_spec.rb +3 -3
- data/spec/sauce_spec_chrome.rb +5 -5
- data/spec/selector_spec.rb +4 -4
- data/spec/selenium_spec_chrome.rb +20 -18
- data/spec/selenium_spec_chrome_remote.rb +15 -19
- data/spec/selenium_spec_edge.rb +19 -6
- data/spec/selenium_spec_firefox.rb +26 -8
- data/spec/selenium_spec_firefox_remote.rb +18 -4
- data/spec/selenium_spec_ie.rb +7 -8
- data/spec/selenium_spec_safari.rb +34 -20
- data/spec/server_spec.rb +19 -7
- data/spec/shared_selenium_node.rb +0 -4
- data/spec/shared_selenium_session.rb +22 -14
- data/spec/spec_helper.rb +36 -3
- data/spec/whitespace_normalizer_spec.rb +54 -0
- data/spec/xpath_builder_spec.rb +1 -1
- metadata +49 -30
- data/lib/capybara/selenium/logger_suppressor.rb +0 -34
- data/lib/capybara/selenium/patches/action_pauser.rb +0 -26
- data/lib/capybara/spec/views/with_title.erb +0 -5
@@ -20,6 +20,8 @@ class Capybara::RackTest::Browser
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def visit(path, **attributes)
|
23
|
+
@new_visit_request = true
|
24
|
+
reset_cache!
|
23
25
|
reset_host!
|
24
26
|
process_and_follow_redirects(:get, path, attributes)
|
25
27
|
end
|
@@ -29,23 +31,29 @@ class Capybara::RackTest::Browser
|
|
29
31
|
request(last_request.fullpath, last_request.env)
|
30
32
|
end
|
31
33
|
|
32
|
-
def submit(method, path, attributes)
|
34
|
+
def submit(method, path, attributes, content_type: nil)
|
33
35
|
path = request_path if path.nil? || path.empty?
|
34
36
|
uri = build_uri(path)
|
35
37
|
uri.query = '' if method.to_s.casecmp('get').zero?
|
36
|
-
|
38
|
+
env = { 'HTTP_REFERER' => referer_url }
|
39
|
+
env['CONTENT_TYPE'] = content_type if content_type
|
40
|
+
process_and_follow_redirects(
|
41
|
+
method,
|
42
|
+
uri.to_s,
|
43
|
+
attributes,
|
44
|
+
env
|
45
|
+
)
|
37
46
|
end
|
38
47
|
|
39
48
|
def follow(method, path, **attributes)
|
40
49
|
return if fragment_or_script?(path)
|
41
50
|
|
42
|
-
process_and_follow_redirects(method, path, attributes, 'HTTP_REFERER' =>
|
51
|
+
process_and_follow_redirects(method, path, attributes, 'HTTP_REFERER' => referer_url)
|
43
52
|
end
|
44
53
|
|
45
54
|
def process_and_follow_redirects(method, path, attributes = {}, env = {})
|
46
55
|
@current_fragment = build_uri(path).fragment
|
47
56
|
process(method, path, attributes, env)
|
48
|
-
|
49
57
|
return unless driver.follow_redirects?
|
50
58
|
|
51
59
|
driver.redirect_limit.times do
|
@@ -69,18 +77,23 @@ class Capybara::RackTest::Browser
|
|
69
77
|
@current_scheme, @current_host, @current_port = new_uri.select(:scheme, :host, :port)
|
70
78
|
@current_fragment = new_uri.fragment || @current_fragment
|
71
79
|
reset_cache!
|
80
|
+
@new_visit_request = false
|
72
81
|
send(method, new_uri.to_s, attributes, env.merge(options[:headers] || {}))
|
73
82
|
end
|
74
83
|
|
75
84
|
def build_uri(path)
|
76
|
-
URI.parse(path)
|
77
|
-
|
78
|
-
uri.path = '/' if uri.path.empty?
|
79
|
-
uri.path = request_path.sub(%r{/[^/]*$}, '/') + uri.path unless uri.path.start_with?('/')
|
85
|
+
uri = URI.parse(path)
|
86
|
+
base_uri = base_relative_uri_for(uri)
|
80
87
|
|
88
|
+
uri.path = base_uri.path + uri.path unless uri.absolute? || uri.path.start_with?('/')
|
89
|
+
|
90
|
+
if base_uri.absolute?
|
91
|
+
base_uri.merge(uri)
|
92
|
+
else
|
81
93
|
uri.scheme ||= @current_scheme
|
82
94
|
uri.host ||= @current_host
|
83
95
|
uri.port ||= @current_port unless uri.default_port == @current_port
|
96
|
+
uri
|
84
97
|
end
|
85
98
|
end
|
86
99
|
|
@@ -123,8 +136,39 @@ class Capybara::RackTest::Browser
|
|
123
136
|
dom.title
|
124
137
|
end
|
125
138
|
|
139
|
+
def last_request
|
140
|
+
raise Rack::Test::Error if @new_visit_request
|
141
|
+
|
142
|
+
super
|
143
|
+
end
|
144
|
+
|
145
|
+
def last_response
|
146
|
+
raise Rack::Test::Error if @new_visit_request
|
147
|
+
|
148
|
+
super
|
149
|
+
end
|
150
|
+
|
126
151
|
protected
|
127
152
|
|
153
|
+
def base_href
|
154
|
+
find(:css, 'head > base').first&.[](:href).to_s
|
155
|
+
end
|
156
|
+
|
157
|
+
def base_relative_uri_for(uri)
|
158
|
+
base_uri = URI.parse(base_href)
|
159
|
+
current_uri = URI.parse(safe_last_request&.url.to_s).tap do |c|
|
160
|
+
c.path.sub!(%r{/[^/]*$}, '/') unless uri.path.empty?
|
161
|
+
c.path = '/' if c.path.empty?
|
162
|
+
end
|
163
|
+
|
164
|
+
if [current_uri, base_uri].any?(&:absolute?)
|
165
|
+
current_uri.merge(base_uri)
|
166
|
+
else
|
167
|
+
base_uri.path = current_uri.path if base_uri.path.empty?
|
168
|
+
base_uri
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
128
172
|
def build_rack_mock_session
|
129
173
|
reset_host! unless current_host
|
130
174
|
Rack::MockSession.new(app, current_host)
|
@@ -136,9 +180,21 @@ protected
|
|
136
180
|
'/'
|
137
181
|
end
|
138
182
|
|
183
|
+
def safe_last_request
|
184
|
+
last_request
|
185
|
+
rescue Rack::Test::Error
|
186
|
+
nil
|
187
|
+
end
|
188
|
+
|
139
189
|
private
|
140
190
|
|
141
191
|
def fragment_or_script?(path)
|
142
192
|
path.gsub(/^#{Regexp.escape(request_path)}/, '').start_with?('#') || path.downcase.start_with?('javascript:')
|
143
193
|
end
|
194
|
+
|
195
|
+
def referer_url
|
196
|
+
build_uri(last_request.url).to_s
|
197
|
+
rescue Rack::Test::Error
|
198
|
+
''
|
199
|
+
end
|
144
200
|
end
|
@@ -98,10 +98,10 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
98
98
|
@browser = nil
|
99
99
|
end
|
100
100
|
|
101
|
-
def get(
|
102
|
-
def post(
|
103
|
-
def put(
|
104
|
-
def delete(
|
101
|
+
def get(...); browser.get(...); end
|
102
|
+
def post(...); browser.post(...); end
|
103
|
+
def put(...); browser.put(...); end
|
104
|
+
def delete(...); browser.delete(...); end
|
105
105
|
def header(key, value); browser.header(key, value); end
|
106
106
|
|
107
107
|
def invalid_element_errors
|
@@ -16,6 +16,8 @@ class Capybara::RackTest::Form < Capybara::RackTest::Node
|
|
16
16
|
def path; @empty_file.path; end
|
17
17
|
def size; 0; end
|
18
18
|
def read; ''; end
|
19
|
+
def append_to(_); end
|
20
|
+
def set_encoding(_); end # rubocop:disable Naming/AccessorMethodName
|
19
21
|
end
|
20
22
|
|
21
23
|
def params(button)
|
@@ -28,19 +30,31 @@ class Capybara::RackTest::Form < Capybara::RackTest::Node
|
|
28
30
|
|
29
31
|
form_elements = native.xpath(form_elements_xpath).reject { |el| submitter?(el) && (el != button.native) }
|
30
32
|
|
31
|
-
form_elements.each_with_object(
|
33
|
+
form_params = form_elements.each_with_object({}.compare_by_identity) do |field, params|
|
32
34
|
case field.name
|
33
35
|
when 'input', 'button' then add_input_param(field, params)
|
34
36
|
when 'select' then add_select_param(field, params)
|
35
37
|
when 'textarea' then add_textarea_param(field, params)
|
36
38
|
end
|
39
|
+
end
|
40
|
+
|
41
|
+
form_params.each_with_object(make_params) do |(name, value), params|
|
42
|
+
merge_param!(params, name, value)
|
37
43
|
end.to_params_hash
|
44
|
+
|
45
|
+
# form_elements.each_with_object(make_params) do |field, params|
|
46
|
+
# case field.name
|
47
|
+
# when 'input', 'button' then add_input_param(field, params)
|
48
|
+
# when 'select' then add_select_param(field, params)
|
49
|
+
# when 'textarea' then add_textarea_param(field, params)
|
50
|
+
# end
|
51
|
+
# end.to_params_hash
|
38
52
|
end
|
39
53
|
|
40
54
|
def submit(button)
|
41
55
|
action = button&.[]('formaction') || native['action']
|
42
56
|
method = button&.[]('formmethod') || request_method
|
43
|
-
driver.submit(method, action.to_s, params(button))
|
57
|
+
driver.submit(method, action.to_s, params(button), content_type: native['enctype'])
|
44
58
|
end
|
45
59
|
|
46
60
|
def multipart?
|
@@ -86,6 +100,8 @@ private
|
|
86
100
|
|
87
101
|
Capybara::RackTest::Node.new(driver, field).value.to_s
|
88
102
|
when 'file'
|
103
|
+
return if value.empty? && params.keys.include?(name) && Rack::Test::VERSION.to_f >= 2.0 # rubocop:disable Performance/InefficientHashSearch
|
104
|
+
|
89
105
|
if multipart?
|
90
106
|
file_to_upload(value)
|
91
107
|
else
|
@@ -94,7 +110,8 @@ private
|
|
94
110
|
else
|
95
111
|
value
|
96
112
|
end
|
97
|
-
merge_param!(params, name, value)
|
113
|
+
# merge_param!(params, name, value)
|
114
|
+
params[name] = value
|
98
115
|
end
|
99
116
|
|
100
117
|
def file_to_upload(filename)
|
@@ -107,18 +124,23 @@ private
|
|
107
124
|
end
|
108
125
|
|
109
126
|
def add_select_param(field, params)
|
127
|
+
name = field['name']
|
110
128
|
if field.has_attribute?('multiple')
|
111
|
-
field.xpath('.//option[@selected]').
|
112
|
-
merge_param!(params, field['name'], (option['value'] || option.text).to_s)
|
129
|
+
value = field.xpath('.//option[@selected]').map do |option|
|
130
|
+
# merge_param!(params, field['name'], (option['value'] || option.text).to_s)
|
131
|
+
(option['value'] || option.text).to_s
|
113
132
|
end
|
133
|
+
params[name] = value unless value.empty?
|
114
134
|
else
|
115
135
|
option = field.xpath('.//option[@selected]').first || field.xpath('.//option').first
|
116
|
-
merge_param!(params, field['name'], (option['value'] || option.text).to_s) if option
|
136
|
+
# merge_param!(params, field['name'], (option['value'] || option.text).to_s) if option
|
137
|
+
params[name] = (option['value'] || option.text).to_s if option
|
117
138
|
end
|
118
139
|
end
|
119
140
|
|
120
141
|
def add_textarea_param(field, params)
|
121
|
-
merge_param!(params, field['name'], field['_capybara_raw_value'].to_s.gsub(/\n/, "\r\n"))
|
142
|
+
# merge_param!(params, field['name'], field['_capybara_raw_value'].to_s.gsub(/\r?\n/, "\r\n"))
|
143
|
+
params[field['name']] = field['_capybara_raw_value'].to_s.gsub(/\r?\n/, "\r\n")
|
122
144
|
end
|
123
145
|
|
124
146
|
def submitter?(el)
|
@@ -1,25 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'capybara/rack_test/errors'
|
4
|
+
require 'capybara/node/whitespace_normalizer'
|
4
5
|
|
5
6
|
class Capybara::RackTest::Node < Capybara::Driver::Node
|
7
|
+
include Capybara::Node::WhitespaceNormalizer
|
8
|
+
|
6
9
|
BLOCK_ELEMENTS = %w[p h1 h2 h3 h4 h5 h6 ol ul pre address blockquote dl div fieldset form hr noscript table].freeze
|
7
10
|
|
8
11
|
def all_text
|
9
|
-
native.text
|
10
|
-
.gsub(/[\u200b\u200e\u200f]/, '')
|
11
|
-
.gsub(/[\ \n\f\t\v\u2028\u2029]+/, ' ')
|
12
|
-
.gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
|
13
|
-
.gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
|
14
|
-
.tr("\u00a0", ' ')
|
12
|
+
normalize_spacing(native.text)
|
15
13
|
end
|
16
14
|
|
17
15
|
def visible_text
|
18
|
-
displayed_text
|
19
|
-
.gsub(/[\ \n]*\n[\ \n]*/, "\n")
|
20
|
-
.gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
|
21
|
-
.gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
|
22
|
-
.tr("\u00a0", ' ')
|
16
|
+
normalize_visible_spacing(displayed_text)
|
23
17
|
end
|
24
18
|
|
25
19
|
def [](name)
|
@@ -108,6 +102,13 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
108
102
|
end
|
109
103
|
end
|
110
104
|
|
105
|
+
def readonly?
|
106
|
+
# readonly attribute not valid on these input types
|
107
|
+
return false if input_field? && %w[hidden range color checkbox radio file submit image reset button].include?(type)
|
108
|
+
|
109
|
+
super
|
110
|
+
end
|
111
|
+
|
111
112
|
def path
|
112
113
|
native.path
|
113
114
|
end
|
@@ -124,23 +125,12 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
124
125
|
alias_method "unchecked_#{meth_name}", meth_name
|
125
126
|
private "unchecked_#{meth_name}" # rubocop:disable Style/AccessModifierDeclarations
|
126
127
|
|
127
|
-
|
128
|
-
|
129
|
-
def #{meth_name}(...)
|
130
|
-
stale_check
|
131
|
-
method(:"unchecked_#{meth_name}").call(...)
|
132
|
-
end
|
133
|
-
METHOD
|
134
|
-
else
|
135
|
-
define_method meth_name do |*args|
|
128
|
+
class_eval <<~METHOD, __FILE__, __LINE__ + 1
|
129
|
+
def #{meth_name}(...)
|
136
130
|
stale_check
|
137
|
-
|
131
|
+
method(:"unchecked_#{meth_name}").call(...)
|
138
132
|
end
|
139
|
-
|
140
|
-
end
|
141
|
-
|
142
|
-
def ==(other)
|
143
|
-
native == other.native
|
133
|
+
METHOD
|
144
134
|
end
|
145
135
|
|
146
136
|
protected
|
@@ -150,16 +140,18 @@ protected
|
|
150
140
|
if !string_node.visible?(check_ancestor)
|
151
141
|
''
|
152
142
|
elsif native.text?
|
153
|
-
native
|
154
|
-
|
155
|
-
|
143
|
+
native
|
144
|
+
.text
|
145
|
+
.delete(REMOVED_CHARACTERS)
|
146
|
+
.tr(SQUEEZED_SPACES, ' ')
|
147
|
+
.squeeze(' ')
|
156
148
|
elsif native.element?
|
157
149
|
text = native.children.map do |child|
|
158
150
|
Capybara::RackTest::Node.new(driver, child).displayed_text(check_ancestor: false)
|
159
151
|
end.join || ''
|
160
152
|
text = "\n#{text}\n" if BLOCK_ELEMENTS.include?(tag_name)
|
161
153
|
text
|
162
|
-
else
|
154
|
+
else # rubocop:disable Lint/DuplicateBranch
|
163
155
|
''
|
164
156
|
end
|
165
157
|
end
|
@@ -213,7 +205,7 @@ private
|
|
213
205
|
min, max, step = (native['min'] || 0).to_f, (native['max'] || 100).to_f, (native['step'] || 1).to_f
|
214
206
|
value = value.to_f
|
215
207
|
value = value.clamp(min, max)
|
216
|
-
value = ((value - min) / step).round * step + min
|
208
|
+
value = (((value - min) / step).round * step) + min
|
217
209
|
native['value'] = value.clamp(min, max)
|
218
210
|
end
|
219
211
|
|
@@ -232,7 +224,14 @@ private
|
|
232
224
|
end
|
233
225
|
native.remove
|
234
226
|
else
|
235
|
-
|
227
|
+
value.to_s.tap do |set_value|
|
228
|
+
if set_value.end_with?("\n") && form&.css('input, textarea')&.count == 1
|
229
|
+
native['value'] = set_value.to_s.chop
|
230
|
+
Capybara::RackTest::Form.new(driver, form).submit(self)
|
231
|
+
else
|
232
|
+
native['value'] = set_value
|
233
|
+
end
|
234
|
+
end
|
236
235
|
end
|
237
236
|
end
|
238
237
|
|
@@ -241,7 +240,7 @@ private
|
|
241
240
|
end
|
242
241
|
|
243
242
|
def follow_link
|
244
|
-
method = self['data-method'] if driver.options[:respect_data_method]
|
243
|
+
method = self['data-method'] || self['data-turbo-method'] if driver.options[:respect_data_method]
|
245
244
|
method ||= :get
|
246
245
|
driver.follow(method, self[:href].to_s)
|
247
246
|
end
|
@@ -16,13 +16,10 @@ module Capybara
|
|
16
16
|
@registered[name] = value
|
17
17
|
end
|
18
18
|
|
19
|
-
def method_missing(method_name,
|
19
|
+
def method_missing(method_name, ...)
|
20
20
|
if @registered.respond_to?(method_name)
|
21
21
|
Capybara::Helpers.warn "DEPRECATED: Calling '#{method_name}' on the drivers/servers container is deprecated without replacement"
|
22
|
-
|
23
|
-
return @registered.public_send(method_name, *args, &block) if options.empty?
|
24
|
-
|
25
|
-
return @registered.public_send(method_name, *args, **options, &block)
|
22
|
+
return @registered.public_send(method_name, ...)
|
26
23
|
end
|
27
24
|
super
|
28
25
|
end
|
@@ -11,32 +11,32 @@ end
|
|
11
11
|
Capybara.register_driver :selenium_headless do |app|
|
12
12
|
version = Capybara::Selenium::Driver.load_selenium
|
13
13
|
options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
|
14
|
-
browser_options =
|
14
|
+
browser_options = Selenium::WebDriver::Firefox::Options.new.tap do |opts|
|
15
15
|
opts.add_argument '-headless'
|
16
16
|
end
|
17
|
-
Capybara::Selenium::Driver.new(app, **
|
17
|
+
Capybara::Selenium::Driver.new(app, **{ :browser => :firefox, options_key => browser_options })
|
18
18
|
end
|
19
19
|
|
20
20
|
Capybara.register_driver :selenium_chrome do |app|
|
21
21
|
version = Capybara::Selenium::Driver.load_selenium
|
22
22
|
options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
|
23
|
-
browser_options =
|
23
|
+
browser_options = Selenium::WebDriver::Chrome::Options.new.tap do |opts|
|
24
24
|
# Workaround https://bugs.chromium.org/p/chromedriver/issues/detail?id=2650&q=load&sort=-id&colspec=ID%20Status%20Pri%20Owner%20Summary
|
25
25
|
opts.add_argument('--disable-site-isolation-trials')
|
26
26
|
end
|
27
27
|
|
28
|
-
Capybara::Selenium::Driver.new(app, **
|
28
|
+
Capybara::Selenium::Driver.new(app, **{ :browser => :chrome, options_key => browser_options })
|
29
29
|
end
|
30
30
|
|
31
31
|
Capybara.register_driver :selenium_chrome_headless do |app|
|
32
32
|
version = Capybara::Selenium::Driver.load_selenium
|
33
33
|
options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
|
34
|
-
browser_options =
|
35
|
-
opts.add_argument('--headless')
|
34
|
+
browser_options = Selenium::WebDriver::Chrome::Options.new.tap do |opts|
|
35
|
+
opts.add_argument('--headless=new')
|
36
36
|
opts.add_argument('--disable-gpu') if Gem.win_platform?
|
37
37
|
# Workaround https://bugs.chromium.org/p/chromedriver/issues/detail?id=2650&q=load&sort=-id&colspec=ID%20Status%20Pri%20Owner%20Summary
|
38
38
|
opts.add_argument('--disable-site-isolation-trials')
|
39
39
|
end
|
40
40
|
|
41
|
-
Capybara::Selenium::Driver.new(app, **
|
41
|
+
Capybara::Selenium::Driver.new(app, **{ :browser => :chrome, options_key => browser_options })
|
42
42
|
end
|
@@ -5,41 +5,62 @@ Capybara.register_server :default do |app, port, _host|
|
|
5
5
|
end
|
6
6
|
|
7
7
|
Capybara.register_server :webrick do |app, port, host, **options|
|
8
|
-
|
8
|
+
base_class = begin
|
9
|
+
require 'rack/handler/webrick'
|
10
|
+
Rack
|
11
|
+
rescue LoadError
|
12
|
+
# Rack 3 separated out the webrick handle - no way test currently in Capybaras automated
|
13
|
+
# tests due to Sinatra not yet supporting Rack 3 - experimental
|
14
|
+
require 'rackup/handler/webrick'
|
15
|
+
Rackup
|
16
|
+
end
|
9
17
|
options = { Host: host, Port: port, AccessLog: [], Logger: WEBrick::Log.new(nil, 0) }.merge(options)
|
10
|
-
|
18
|
+
base_class::Handler::WEBrick.run(app, **options)
|
11
19
|
end
|
12
20
|
|
13
|
-
Capybara.register_server :puma do |app, port, host, **options|
|
21
|
+
Capybara.register_server :puma do |app, port, host, **options| # rubocop:disable Metrics/BlockLength
|
22
|
+
begin
|
23
|
+
require 'rackup'
|
24
|
+
rescue LoadError # rubocop:disable Lint/SuppressedException
|
25
|
+
end
|
14
26
|
begin
|
15
27
|
require 'rack/handler/puma'
|
16
28
|
rescue LoadError
|
17
29
|
raise LoadError, 'Capybara is unable to load `puma` for its server, please add `puma` to your project or specify a different server via something like `Capybara.server = :webrick`.'
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
30
|
+
end
|
31
|
+
puma_rack_handler = defined?(Rackup::Handler::Puma) ? Rackup::Handler::Puma : Rack::Handler::Puma
|
32
|
+
|
33
|
+
unless puma_rack_handler.respond_to?(:config)
|
34
|
+
raise LoadError, 'Capybara requires `puma` version 3.8.0 or higher, please upgrade `puma` or register and specify your own server block'
|
22
35
|
end
|
23
36
|
|
24
37
|
# If we just run the Puma Rack handler it installs signal handlers which prevent us from being able to interrupt tests.
|
25
38
|
# Therefore construct and run the Server instance ourselves.
|
26
|
-
#
|
39
|
+
# puma_rack_handler.run(app, { Host: host, Port: port, Threads: "0:4", workers: 0, daemon: false }.merge(options))
|
27
40
|
default_options = { Host: host, Port: port, Threads: '0:4', workers: 0, daemon: false }
|
28
41
|
options = default_options.merge(options)
|
29
42
|
|
30
|
-
conf =
|
43
|
+
conf = puma_rack_handler.config(app, options)
|
31
44
|
conf.clamp
|
32
|
-
events = conf.options[:Silent] ? ::Puma::Events.strings : ::Puma::Events.stdio
|
33
45
|
|
34
46
|
puma_ver = Gem::Version.new(Puma::Const::PUMA_VERSION)
|
35
47
|
require_relative 'patches/puma_ssl' if Gem::Requirement.new('>=4.0.0', '< 4.1.0').satisfied_by?(puma_ver)
|
36
48
|
|
37
|
-
|
38
|
-
|
39
|
-
|
49
|
+
logger = (defined?(Puma::LogWriter) ? Puma::LogWriter : Puma::Events).then do |cls|
|
50
|
+
conf.options[:Silent] ? cls.strings : cls.stdio
|
51
|
+
end
|
52
|
+
conf.options[:log_writer] = logger
|
53
|
+
|
54
|
+
logger.log 'Capybara starting Puma...'
|
55
|
+
logger.log "* Version #{Puma::Const::PUMA_VERSION}, codename: #{Puma::Const::CODE_NAME}"
|
56
|
+
logger.log "* Min threads: #{conf.options[:min_threads]}, max threads: #{conf.options[:max_threads]}"
|
40
57
|
|
41
|
-
Puma::Server.new(
|
42
|
-
|
43
|
-
|
58
|
+
Puma::Server.new(
|
59
|
+
conf.app,
|
60
|
+
defined?(Puma::LogWriter) ? nil : logger,
|
61
|
+
conf.options
|
62
|
+
).tap do |s|
|
63
|
+
s.binder.parse conf.options[:binds], (s.log_writer rescue s.events) # rubocop:disable Style/RescueModifier
|
64
|
+
s.min_threads, s.max_threads = conf.options[:min_threads], conf.options[:max_threads] if s.respond_to? :min_threads=
|
44
65
|
end.run.join
|
45
66
|
end
|
data/lib/capybara/result.rb
CHANGED
@@ -34,7 +34,7 @@ module Capybara
|
|
34
34
|
@allow_reload = false
|
35
35
|
end
|
36
36
|
|
37
|
-
def_delegators :full_results, :size, :length, :last, :values_at, :inspect, :sample
|
37
|
+
def_delegators :full_results, :size, :length, :last, :values_at, :inspect, :sample, :to_ary
|
38
38
|
|
39
39
|
alias index find_index
|
40
40
|
|
@@ -116,7 +116,7 @@ module Capybara
|
|
116
116
|
message << ' but there were no matches'
|
117
117
|
else
|
118
118
|
message << ", found #{count} #{Capybara::Helpers.declension('match', 'matches', count)}: " \
|
119
|
-
<< full_results.map
|
119
|
+
<< full_results.map { |r| r.text.inspect }.join(', ')
|
120
120
|
end
|
121
121
|
unless rest.empty?
|
122
122
|
elements = rest.map { |el| el.text rescue '<<ERROR>>' }.map(&:inspect).join(', ') # rubocop:disable Style/RescueModifier
|
@@ -23,7 +23,7 @@ end
|
|
23
23
|
if RUBY_ENGINE == 'jruby'
|
24
24
|
# :nocov:
|
25
25
|
module Capybara::DSL
|
26
|
-
class <<self
|
26
|
+
class << self
|
27
27
|
remove_method :included
|
28
28
|
|
29
29
|
def included(base)
|
@@ -36,7 +36,7 @@ if RUBY_ENGINE == 'jruby'
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
if defined?(
|
39
|
+
if defined?(RSpec::Matchers)
|
40
40
|
module ::RSpec::Matchers
|
41
41
|
def self.included(base)
|
42
42
|
base.send(:include, ::Capybara::RSpecMatcherProxies) if base.include?(::Capybara::DSL)
|
@@ -55,7 +55,7 @@ else
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def self.prepended(base)
|
58
|
-
class <<base
|
58
|
+
class << base
|
59
59
|
prepend ClassMethods
|
60
60
|
end
|
61
61
|
end
|
@@ -70,13 +70,13 @@ else
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def self.prepended(base)
|
73
|
-
class <<base
|
73
|
+
class << base
|
74
74
|
prepend ClassMethods
|
75
75
|
end
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
79
|
-
Capybara::DSL.prepend
|
79
|
+
Capybara::DSL.prepend Capybara::DSLRSpecProxyInstaller
|
80
80
|
|
81
|
-
|
81
|
+
RSpec::Matchers.prepend Capybara::RSpecMatcherProxyInstaller if defined?(RSpec::Matchers)
|
82
82
|
end
|
@@ -47,14 +47,16 @@ module Capybara
|
|
47
47
|
end
|
48
48
|
|
49
49
|
class WrappedElementMatcher < Base
|
50
|
-
def matches?(actual)
|
50
|
+
def matches?(actual, &filter_block)
|
51
|
+
@filter_block ||= filter_block
|
51
52
|
element_matches?(wrap(actual))
|
52
53
|
rescue Capybara::ExpectationNotMet => e
|
53
54
|
@failure_message = e.message
|
54
55
|
false
|
55
56
|
end
|
56
57
|
|
57
|
-
def does_not_match?(actual)
|
58
|
+
def does_not_match?(actual, &filter_block)
|
59
|
+
@filter_block ||= filter_block
|
58
60
|
element_does_not_match?(wrap(actual))
|
59
61
|
rescue Capybara::ExpectationNotMet => e
|
60
62
|
@failure_message_when_negated = e.message
|
@@ -86,12 +88,12 @@ module Capybara
|
|
86
88
|
@matcher = matcher
|
87
89
|
end
|
88
90
|
|
89
|
-
def matches?(actual)
|
90
|
-
@matcher.does_not_match?(actual)
|
91
|
+
def matches?(actual, &filter_block)
|
92
|
+
@matcher.does_not_match?(actual, &filter_block)
|
91
93
|
end
|
92
94
|
|
93
|
-
def does_not_match?(actual)
|
94
|
-
@matcher.matches?(actual)
|
95
|
+
def does_not_match?(actual, &filter_block)
|
96
|
+
@matcher.matches?(actual, &filter_block)
|
95
97
|
end
|
96
98
|
|
97
99
|
def description
|
@@ -8,10 +8,10 @@ module Capybara
|
|
8
8
|
class HaveSelector < CountableWrappedElementMatcher
|
9
9
|
def initialize(*args, **kw_args, &filter_block)
|
10
10
|
super
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
return unless (@args.size < 2) && @kw_args.keys.any?(String)
|
12
|
+
|
13
|
+
@args.push(@kw_args)
|
14
|
+
@kw_args = {}
|
15
15
|
end
|
16
16
|
|
17
17
|
def element_matches?(el)
|
@@ -22,9 +22,7 @@ module Capybara
|
|
22
22
|
el.assert_no_selector(*@args, **session_query_options, &@filter_block)
|
23
23
|
end
|
24
24
|
|
25
|
-
def description
|
26
|
-
"have #{query.description}"
|
27
|
-
end
|
25
|
+
def description = "have #{query.description}"
|
28
26
|
|
29
27
|
def query
|
30
28
|
@query ||= Capybara::Queries::SelectorQuery.new(*session_query_args, **session_query_options, &@filter_block)
|
@@ -40,9 +38,7 @@ module Capybara
|
|
40
38
|
raise ArgumentError, 'The have_all_selectors matcher does not support use with not_to/should_not'
|
41
39
|
end
|
42
40
|
|
43
|
-
def description
|
44
|
-
'have all selectors'
|
45
|
-
end
|
41
|
+
def description = 'have all selectors'
|
46
42
|
end
|
47
43
|
|
48
44
|
class HaveNoSelectors < WrappedElementMatcher
|
@@ -54,9 +50,7 @@ module Capybara
|
|
54
50
|
raise ArgumentError, 'The have_none_of_selectors matcher does not support use with not_to/should_not'
|
55
51
|
end
|
56
52
|
|
57
|
-
def description
|
58
|
-
'have no selectors'
|
59
|
-
end
|
53
|
+
def description = 'have no selectors'
|
60
54
|
end
|
61
55
|
|
62
56
|
class HaveAnySelectors < WrappedElementMatcher
|
@@ -64,13 +58,11 @@ module Capybara
|
|
64
58
|
el.assert_any_of_selectors(*@args, **session_query_options, &@filter_block)
|
65
59
|
end
|
66
60
|
|
67
|
-
def does_not_match?(
|
61
|
+
def does_not_match?(el)
|
68
62
|
el.assert_none_of_selectors(*@args, **session_query_options, &@filter_block)
|
69
63
|
end
|
70
64
|
|
71
|
-
def description
|
72
|
-
'have any selectors'
|
73
|
-
end
|
65
|
+
def description = 'have any selectors'
|
74
66
|
end
|
75
67
|
end
|
76
68
|
end
|