capybara 3.30.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 +153 -13
- data/README.md +9 -4
- data/lib/capybara.rb +18 -8
- data/lib/capybara/config.rb +4 -6
- data/lib/capybara/cucumber.rb +1 -1
- data/lib/capybara/driver/base.rb +4 -0
- data/lib/capybara/dsl.rb +10 -2
- data/lib/capybara/helpers.rb +25 -1
- data/lib/capybara/minitest.rb +232 -144
- data/lib/capybara/minitest/spec.rb +156 -97
- data/lib/capybara/node/actions.rb +16 -21
- data/lib/capybara/node/base.rb +6 -6
- data/lib/capybara/node/element.rb +14 -13
- data/lib/capybara/node/finders.rb +12 -7
- data/lib/capybara/node/matchers.rb +36 -27
- data/lib/capybara/node/simple.rb +6 -2
- data/lib/capybara/queries/ancestor_query.rb +1 -1
- 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 +40 -18
- data/lib/capybara/queries/sibling_query.rb +1 -1
- data/lib/capybara/queries/style_query.rb +1 -1
- data/lib/capybara/queries/text_query.rb +7 -1
- data/lib/capybara/rack_test/browser.rb +9 -3
- data/lib/capybara/rack_test/driver.rb +1 -0
- data/lib/capybara/rack_test/form.rb +1 -1
- data/lib/capybara/rack_test/node.rb +35 -10
- data/lib/capybara/registration_container.rb +44 -0
- data/lib/capybara/registrations/drivers.rb +18 -12
- data/lib/capybara/registrations/patches/puma_ssl.rb +3 -1
- data/lib/capybara/registrations/servers.rb +3 -2
- data/lib/capybara/result.rb +35 -15
- data/lib/capybara/rspec.rb +2 -0
- data/lib/capybara/rspec/matcher_proxies.rb +5 -5
- data/lib/capybara/rspec/matchers.rb +33 -32
- data/lib/capybara/rspec/matchers/base.rb +12 -6
- data/lib/capybara/rspec/matchers/count_sugar.rb +2 -1
- data/lib/capybara/rspec/matchers/have_ancestor.rb +4 -3
- data/lib/capybara/rspec/matchers/have_current_path.rb +2 -2
- data/lib/capybara/rspec/matchers/have_selector.rb +15 -7
- data/lib/capybara/rspec/matchers/have_sibling.rb +3 -3
- data/lib/capybara/rspec/matchers/have_text.rb +3 -3
- 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 +2 -1
- data/lib/capybara/selector.rb +14 -3
- data/lib/capybara/selector/builders/css_builder.rb +1 -1
- data/lib/capybara/selector/builders/xpath_builder.rb +3 -1
- data/lib/capybara/selector/definition.rb +11 -9
- data/lib/capybara/selector/definition/button.rb +26 -14
- data/lib/capybara/selector/definition/css.rb +1 -1
- data/lib/capybara/selector/definition/datalist_input.rb +1 -1
- data/lib/capybara/selector/definition/element.rb +2 -1
- data/lib/capybara/selector/definition/fillable_field.rb +1 -1
- data/lib/capybara/selector/definition/label.rb +2 -2
- data/lib/capybara/selector/definition/link.rb +8 -0
- data/lib/capybara/selector/definition/select.rb +32 -13
- data/lib/capybara/selector/definition/table.rb +1 -1
- data/lib/capybara/selector/definition/table_row.rb +2 -2
- data/lib/capybara/selector/filter_set.rb +2 -2
- data/lib/capybara/selector/selector.rb +9 -1
- data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -1
- data/lib/capybara/selenium/atoms/src/getAttribute.js +1 -1
- data/lib/capybara/selenium/atoms/src/isDisplayed.js +1 -1
- data/lib/capybara/selenium/driver.rb +52 -7
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +10 -12
- data/lib/capybara/selenium/driver_specializations/edge_driver.rb +9 -11
- data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +3 -3
- data/lib/capybara/selenium/extensions/find.rb +4 -4
- data/lib/capybara/selenium/extensions/html5_drag.rb +24 -8
- data/lib/capybara/selenium/extensions/scroll.rb +8 -10
- data/lib/capybara/selenium/logger_suppressor.rb +8 -2
- data/lib/capybara/selenium/node.rb +96 -16
- data/lib/capybara/selenium/nodes/chrome_node.rb +27 -16
- data/lib/capybara/selenium/nodes/edge_node.rb +1 -1
- data/lib/capybara/selenium/nodes/firefox_node.rb +9 -4
- data/lib/capybara/selenium/nodes/safari_node.rb +1 -1
- data/lib/capybara/selenium/patches/action_pauser.rb +26 -0
- data/lib/capybara/selenium/patches/atoms.rb +4 -4
- data/lib/capybara/selenium/patches/logs.rb +7 -9
- data/lib/capybara/server/animation_disabler.rb +8 -3
- data/lib/capybara/server/middleware.rb +4 -2
- data/lib/capybara/session.rb +53 -29
- data/lib/capybara/session/config.rb +3 -1
- data/lib/capybara/session/matchers.rb +11 -11
- data/lib/capybara/spec/public/test.js +64 -7
- data/lib/capybara/spec/session/accept_alert_spec.rb +1 -1
- data/lib/capybara/spec/session/all_spec.rb +45 -5
- data/lib/capybara/spec/session/assert_text_spec.rb +5 -5
- data/lib/capybara/spec/session/check_spec.rb +6 -0
- data/lib/capybara/spec/session/click_button_spec.rb +11 -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 +11 -8
- data/lib/capybara/spec/session/has_button_spec.rb +51 -0
- data/lib/capybara/spec/session/has_css_spec.rb +14 -10
- data/lib/capybara/spec/session/has_current_path_spec.rb +15 -2
- data/lib/capybara/spec/session/has_field_spec.rb +16 -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_text_spec.rb +5 -12
- 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 +169 -33
- data/lib/capybara/spec/session/refresh_spec.rb +2 -1
- data/lib/capybara/spec/session/save_page_spec.rb +4 -4
- 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 +13 -14
- data/lib/capybara/spec/test_app.rb +23 -21
- data/lib/capybara/spec/views/form.erb +36 -3
- data/lib/capybara/spec/views/with_animation.erb +8 -0
- data/lib/capybara/spec/views/with_dragula.erb +3 -1
- data/lib/capybara/spec/views/with_html.erb +2 -2
- data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
- data/lib/capybara/spec/views/with_js.erb +3 -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 +9 -8
- data/spec/capybara_spec.rb +1 -1
- data/spec/dsl_spec.rb +14 -1
- data/spec/fixtures/selenium_driver_rspec_success.rb +1 -1
- data/spec/minitest_spec.rb +3 -2
- data/spec/rack_test_spec.rb +28 -6
- data/spec/regexp_dissassembler_spec.rb +0 -4
- data/spec/result_spec.rb +40 -29
- data/spec/rspec/features_spec.rb +3 -1
- data/spec/rspec/scenarios_spec.rb +4 -0
- data/spec/rspec/shared_spec_matchers.rb +63 -51
- data/spec/rspec_spec.rb +4 -0
- data/spec/selector_spec.rb +17 -2
- data/spec/selenium_spec_chrome.rb +45 -21
- data/spec/selenium_spec_chrome_remote.rb +7 -1
- data/spec/selenium_spec_firefox.rb +15 -13
- data/spec/server_spec.rb +60 -49
- data/spec/shared_selenium_node.rb +18 -0
- data/spec/shared_selenium_session.rb +98 -7
- data/spec/spec_helper.rb +1 -1
- metadata +50 -14
- data/lib/capybara/spec/session/source_spec.rb +0 -0
@@ -8,6 +8,7 @@ class Capybara::RackTest::Browser
|
|
8
8
|
|
9
9
|
def initialize(driver)
|
10
10
|
@driver = driver
|
11
|
+
@current_fragment = nil
|
11
12
|
end
|
12
13
|
|
13
14
|
def app
|
@@ -30,7 +31,9 @@ class Capybara::RackTest::Browser
|
|
30
31
|
|
31
32
|
def submit(method, path, attributes)
|
32
33
|
path = request_path if path.nil? || path.empty?
|
33
|
-
|
34
|
+
uri = build_uri(path)
|
35
|
+
uri.query = '' if method.to_s.casecmp('get').zero?
|
36
|
+
process_and_follow_redirects(method, uri.to_s, attributes, 'HTTP_REFERER' => current_url)
|
34
37
|
end
|
35
38
|
|
36
39
|
def follow(method, path, **attributes)
|
@@ -40,6 +43,7 @@ class Capybara::RackTest::Browser
|
|
40
43
|
end
|
41
44
|
|
42
45
|
def process_and_follow_redirects(method, path, attributes = {}, env = {})
|
46
|
+
@current_fragment = build_uri(path).fragment
|
43
47
|
process(method, path, attributes, env)
|
44
48
|
|
45
49
|
return unless driver.follow_redirects?
|
@@ -63,7 +67,7 @@ class Capybara::RackTest::Browser
|
|
63
67
|
method = method.downcase
|
64
68
|
new_uri = build_uri(path)
|
65
69
|
@current_scheme, @current_host, @current_port = new_uri.select(:scheme, :host, :port)
|
66
|
-
|
70
|
+
@current_fragment = new_uri.fragment || @current_fragment
|
67
71
|
reset_cache!
|
68
72
|
send(method, new_uri.to_s, attributes, env.merge(options[:headers] || {}))
|
69
73
|
end
|
@@ -81,7 +85,9 @@ class Capybara::RackTest::Browser
|
|
81
85
|
end
|
82
86
|
|
83
87
|
def current_url
|
84
|
-
last_request.url
|
88
|
+
uri = build_uri(last_request.url)
|
89
|
+
uri.fragment = @current_fragment if @current_fragment
|
90
|
+
uri.to_s
|
85
91
|
rescue Rack::Test::Error
|
86
92
|
''
|
87
93
|
end
|
@@ -17,6 +17,7 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
17
17
|
def initialize(app, **options)
|
18
18
|
raise ArgumentError, 'rack-test requires a rack application, but none was given' unless app
|
19
19
|
|
20
|
+
super()
|
20
21
|
@app = app
|
21
22
|
@options = DEFAULT_OPTIONS.merge(options)
|
22
23
|
end
|
@@ -6,7 +6,7 @@ class Capybara::RackTest::Form < Capybara::RackTest::Node
|
|
6
6
|
# That check should be based solely on the form element's 'enctype' attribute value,
|
7
7
|
# which should probably be provided to Rack::Test in its non-GET request methods.
|
8
8
|
class NilUploadedFile < Rack::Test::UploadedFile
|
9
|
-
def initialize
|
9
|
+
def initialize # rubocop:disable Lint/MissingSuper
|
10
10
|
@empty_file = Tempfile.new('nil_uploaded_file')
|
11
11
|
@empty_file.close
|
12
12
|
end
|
@@ -15,7 +15,7 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def visible_text
|
18
|
-
displayed_text.
|
18
|
+
displayed_text.squeeze(' ')
|
19
19
|
.gsub(/[\ \n]*\n[\ \n]*/, "\n")
|
20
20
|
.gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
|
21
21
|
.gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
|
@@ -45,6 +45,7 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
45
45
|
|
46
46
|
if radio? then set_radio(value)
|
47
47
|
elsif checkbox? then set_checkbox(value)
|
48
|
+
elsif range? then set_range(value)
|
48
49
|
elsif input_field? then set_input(value)
|
49
50
|
elsif textarea? then native['_capybara_raw_value'] = value.to_s
|
50
51
|
end
|
@@ -76,8 +77,8 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
76
77
|
set(!checked?)
|
77
78
|
elsif tag_name == 'label'
|
78
79
|
click_label
|
79
|
-
elsif
|
80
|
-
toggle_details
|
80
|
+
elsif (details = native.xpath('.//ancestor-or-self::details').last)
|
81
|
+
toggle_details(details)
|
81
82
|
end
|
82
83
|
end
|
83
84
|
|
@@ -123,9 +124,18 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
123
124
|
alias_method "unchecked_#{meth_name}", meth_name
|
124
125
|
private "unchecked_#{meth_name}" # rubocop:disable Style/AccessModifierDeclarations
|
125
126
|
|
126
|
-
|
127
|
-
|
128
|
-
|
127
|
+
if RUBY_VERSION >= '2.7'
|
128
|
+
class_eval <<~METHOD, __FILE__, __LINE__ + 1
|
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|
|
136
|
+
stale_check
|
137
|
+
send("unchecked_#{meth_name}", *args)
|
138
|
+
end
|
129
139
|
end
|
130
140
|
end
|
131
141
|
|
@@ -199,6 +209,14 @@ private
|
|
199
209
|
end
|
200
210
|
end
|
201
211
|
|
212
|
+
def set_range(value) # rubocop:disable Naming/AccessorMethodName
|
213
|
+
min, max, step = (native['min'] || 0).to_f, (native['max'] || 100).to_f, (native['step'] || 1).to_f
|
214
|
+
value = value.to_f
|
215
|
+
value = value.clamp(min, max)
|
216
|
+
value = ((value - min) / step).round * step + min
|
217
|
+
native['value'] = value.clamp(min, max)
|
218
|
+
end
|
219
|
+
|
202
220
|
def set_input(value) # rubocop:disable Naming/AccessorMethodName
|
203
221
|
if text_or_password? && attribute_is_not_blank?(:maxlength)
|
204
222
|
# Browser behavior for maxlength="0" is inconsistent, so we stick with
|
@@ -238,11 +256,14 @@ private
|
|
238
256
|
labelled_control.set(!labelled_control.checked?) if checkbox_or_radio?(labelled_control)
|
239
257
|
end
|
240
258
|
|
241
|
-
def toggle_details
|
242
|
-
|
243
|
-
|
259
|
+
def toggle_details(details = nil)
|
260
|
+
details ||= native.xpath('.//ancestor-or-self::details').last
|
261
|
+
return unless details
|
262
|
+
|
263
|
+
if details.has_attribute?('open')
|
264
|
+
details.remove_attribute('open')
|
244
265
|
else
|
245
|
-
|
266
|
+
details.set_attribute('open', 'open')
|
246
267
|
end
|
247
268
|
end
|
248
269
|
|
@@ -284,6 +305,10 @@ protected
|
|
284
305
|
tag_name == 'textarea'
|
285
306
|
end
|
286
307
|
|
308
|
+
def range?
|
309
|
+
input_field? && type == 'range'
|
310
|
+
end
|
311
|
+
|
287
312
|
OPTION_OWNER_XPATH = XPath.parent(:optgroup, :select, :datalist).to_s.freeze
|
288
313
|
DISABLED_BY_FIELDSET_XPATH = XPath.generate do |x|
|
289
314
|
x.parent(:fieldset)[
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Capybara
|
4
|
+
# @api private
|
5
|
+
class RegistrationContainer
|
6
|
+
def names
|
7
|
+
@registered.keys
|
8
|
+
end
|
9
|
+
|
10
|
+
def [](name)
|
11
|
+
@registered[name]
|
12
|
+
end
|
13
|
+
|
14
|
+
def []=(name, value)
|
15
|
+
Capybara::Helpers.warn 'DEPRECATED: Directly setting drivers/servers is deprecated, please use Capybara.register_driver/register_server instead'
|
16
|
+
@registered[name] = value
|
17
|
+
end
|
18
|
+
|
19
|
+
def method_missing(method_name, *args, **options, &block)
|
20
|
+
if @registered.respond_to?(method_name)
|
21
|
+
Capybara::Helpers.warn "DEPRECATED: Calling '#{method_name}' on the drivers/servers container is deprecated without replacement"
|
22
|
+
# RUBY 2.6 will send an empty hash rather than nothing with **options so fix that
|
23
|
+
return @registered.public_send(method_name, *args, &block) if options.empty?
|
24
|
+
|
25
|
+
return @registered.public_send(method_name, *args, **options, &block)
|
26
|
+
end
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
30
|
+
def respond_to_missing?(method_name, include_all)
|
31
|
+
@registered.respond_to?(method_name) || super
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def initialize
|
37
|
+
@registered = {}
|
38
|
+
end
|
39
|
+
|
40
|
+
def register(name, block)
|
41
|
+
@registered[name] = block
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -9,28 +9,34 @@ Capybara.register_driver :selenium do |app|
|
|
9
9
|
end
|
10
10
|
|
11
11
|
Capybara.register_driver :selenium_headless do |app|
|
12
|
-
Capybara::Selenium::Driver.load_selenium
|
13
|
-
|
14
|
-
browser_options.
|
15
|
-
|
12
|
+
version = Capybara::Selenium::Driver.load_selenium
|
13
|
+
options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
|
14
|
+
browser_options = ::Selenium::WebDriver::Firefox::Options.new.tap do |opts|
|
15
|
+
opts.add_argument '-headless'
|
16
|
+
end
|
17
|
+
Capybara::Selenium::Driver.new(app, **Hash[:browser => :firefox, options_key => browser_options])
|
16
18
|
end
|
17
19
|
|
18
20
|
Capybara.register_driver :selenium_chrome do |app|
|
19
|
-
Capybara::Selenium::Driver.load_selenium
|
21
|
+
version = Capybara::Selenium::Driver.load_selenium
|
22
|
+
options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
|
20
23
|
browser_options = ::Selenium::WebDriver::Chrome::Options.new.tap do |opts|
|
21
24
|
# Workaround https://bugs.chromium.org/p/chromedriver/issues/detail?id=2650&q=load&sort=-id&colspec=ID%20Status%20Pri%20Owner%20Summary
|
22
|
-
opts.
|
25
|
+
opts.add_argument('--disable-site-isolation-trials')
|
23
26
|
end
|
24
|
-
|
27
|
+
|
28
|
+
Capybara::Selenium::Driver.new(app, **Hash[:browser => :chrome, options_key => browser_options])
|
25
29
|
end
|
26
30
|
|
27
31
|
Capybara.register_driver :selenium_chrome_headless do |app|
|
28
|
-
Capybara::Selenium::Driver.load_selenium
|
32
|
+
version = Capybara::Selenium::Driver.load_selenium
|
33
|
+
options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
|
29
34
|
browser_options = ::Selenium::WebDriver::Chrome::Options.new.tap do |opts|
|
30
|
-
opts.
|
31
|
-
opts.
|
35
|
+
opts.add_argument('--headless')
|
36
|
+
opts.add_argument('--disable-gpu') if Gem.win_platform?
|
32
37
|
# Workaround https://bugs.chromium.org/p/chromedriver/issues/detail?id=2650&q=load&sort=-id&colspec=ID%20Status%20Pri%20Owner%20Summary
|
33
|
-
opts.
|
38
|
+
opts.add_argument('--disable-site-isolation-trials')
|
34
39
|
end
|
35
|
-
|
40
|
+
|
41
|
+
Capybara::Selenium::Driver.new(app, **Hash[:browser => :chrome, options_key => browser_options])
|
36
42
|
end
|
@@ -4,12 +4,14 @@ module Puma
|
|
4
4
|
module MiniSSL
|
5
5
|
class Socket
|
6
6
|
def read_nonblock(size, *_)
|
7
|
+
wait_states = %i[wait_readable wait_writable]
|
8
|
+
|
7
9
|
loop do
|
8
10
|
output = engine_read_all
|
9
11
|
return output if output
|
10
12
|
|
11
13
|
data = @socket.read_nonblock(size, exception: false)
|
12
|
-
raise IO::EAGAINWaitReadable if
|
14
|
+
raise IO::EAGAINWaitReadable if wait_states.include? data
|
13
15
|
return nil if data.nil?
|
14
16
|
|
15
17
|
@engine.inject(data)
|
@@ -7,7 +7,7 @@ end
|
|
7
7
|
Capybara.register_server :webrick do |app, port, host, **options|
|
8
8
|
require 'rack/handler/webrick'
|
9
9
|
options = { Host: host, Port: port, AccessLog: [], Logger: WEBrick::Log.new(nil, 0) }.merge(options)
|
10
|
-
Rack::Handler::WEBrick.run(app, options)
|
10
|
+
Rack::Handler::WEBrick.run(app, **options)
|
11
11
|
end
|
12
12
|
|
13
13
|
Capybara.register_server :puma do |app, port, host, **options|
|
@@ -28,10 +28,11 @@ Capybara.register_server :puma do |app, port, host, **options|
|
|
28
28
|
options = default_options.merge(options)
|
29
29
|
|
30
30
|
conf = Rack::Handler::Puma.config(app, options)
|
31
|
+
conf.clamp
|
31
32
|
events = conf.options[:Silent] ? ::Puma::Events.strings : ::Puma::Events.stdio
|
32
33
|
|
33
34
|
puma_ver = Gem::Version.new(Puma::Const::PUMA_VERSION)
|
34
|
-
require_relative 'patches/puma_ssl' if
|
35
|
+
require_relative 'patches/puma_ssl' if Gem::Requirement.new('>=4.0.0', '< 4.1.0').satisfied_by?(puma_ver)
|
35
36
|
|
36
37
|
events.log 'Capybara starting Puma...'
|
37
38
|
events.log "* Version #{Puma::Const::PUMA_VERSION} , codename: #{Puma::Const::CODE_NAME}"
|
data/lib/capybara/result.rb
CHANGED
@@ -31,6 +31,7 @@ module Capybara
|
|
31
31
|
@filter_errors = []
|
32
32
|
@results_enum = lazy_select_elements { |node| query.matches_filters?(node, @filter_errors) }
|
33
33
|
@query = query
|
34
|
+
@allow_reload = false
|
34
35
|
end
|
35
36
|
|
36
37
|
def_delegators :full_results, :size, :length, :last, :values_at, :inspect, :sample
|
@@ -38,12 +39,12 @@ module Capybara
|
|
38
39
|
alias index find_index
|
39
40
|
|
40
41
|
def each(&block)
|
41
|
-
return enum_for(:each) unless
|
42
|
+
return enum_for(:each) unless block
|
42
43
|
|
43
44
|
@result_cache.each(&block)
|
44
45
|
loop do
|
45
46
|
next_result = @results_enum.next
|
46
|
-
|
47
|
+
add_to_cache(next_result)
|
47
48
|
yield next_result
|
48
49
|
end
|
49
50
|
self
|
@@ -53,13 +54,18 @@ module Capybara
|
|
53
54
|
idx, length = args
|
54
55
|
max_idx = case idx
|
55
56
|
when Integer
|
56
|
-
if
|
57
|
-
length.nil? ? idx : idx + length - 1
|
58
|
-
else
|
57
|
+
if idx.negative?
|
59
58
|
nil
|
59
|
+
else
|
60
|
+
length.nil? ? idx : idx + length - 1
|
60
61
|
end
|
61
62
|
when Range
|
62
|
-
|
63
|
+
# idx.max is broken with beginless ranges
|
64
|
+
# idx.end && idx.max # endless range will have end == nil
|
65
|
+
max = idx.end
|
66
|
+
max = nil if max&.negative?
|
67
|
+
max -= 1 if max && idx.exclude_end?
|
68
|
+
max
|
63
69
|
end
|
64
70
|
|
65
71
|
if max_idx.nil?
|
@@ -85,16 +91,14 @@ module Capybara
|
|
85
91
|
return load_up_to(count + 1) <=> count
|
86
92
|
end
|
87
93
|
|
88
|
-
if min && (min = Integer(min))
|
89
|
-
return -1 if load_up_to(min) < min
|
90
|
-
end
|
94
|
+
return -1 if min && (min = Integer(min)) && (load_up_to(min) < min)
|
91
95
|
|
92
|
-
if max && (max = Integer(max))
|
93
|
-
return 1 if load_up_to(max + 1) > max
|
94
|
-
end
|
96
|
+
return 1 if max && (max = Integer(max)) && (load_up_to(max + 1) > max)
|
95
97
|
|
96
98
|
if between
|
97
|
-
min, max = between.
|
99
|
+
min, max = (between.begin && between.min) || 1, between.end
|
100
|
+
max -= 1 if max && between.exclude_end?
|
101
|
+
|
98
102
|
size = load_up_to(max ? max + 1 : min)
|
99
103
|
return size <=> min unless between.include?(size)
|
100
104
|
end
|
@@ -130,13 +134,26 @@ module Capybara
|
|
130
134
|
@elements.length
|
131
135
|
end
|
132
136
|
|
137
|
+
##
|
138
|
+
# @api private
|
139
|
+
#
|
140
|
+
def allow_reload!
|
141
|
+
@allow_reload = true
|
142
|
+
self
|
143
|
+
end
|
144
|
+
|
133
145
|
private
|
134
146
|
|
147
|
+
def add_to_cache(elem)
|
148
|
+
elem.allow_reload!(@result_cache.size) if @allow_reload
|
149
|
+
@result_cache << elem
|
150
|
+
end
|
151
|
+
|
135
152
|
def load_up_to(num)
|
136
153
|
loop do
|
137
154
|
break if @result_cache.size >= num
|
138
155
|
|
139
|
-
@
|
156
|
+
add_to_cache(@results_enum.next)
|
140
157
|
end
|
141
158
|
@result_cache.size
|
142
159
|
end
|
@@ -150,10 +167,13 @@ module Capybara
|
|
150
167
|
@rest ||= @elements - full_results
|
151
168
|
end
|
152
169
|
|
153
|
-
if
|
170
|
+
if RUBY_PLATFORM == 'java'
|
154
171
|
# JRuby < 9.2.8.0 has an issue with lazy enumerators which
|
155
172
|
# causes a concurrency issue with network requests here
|
156
173
|
# https://github.com/jruby/jruby/issues/4212
|
174
|
+
# while JRuby >= 9.2.8.0 leaks threads when using lazy enumerators
|
175
|
+
# https://github.com/teamcapybara/capybara/issues/2349
|
176
|
+
# so disable the use and JRuby users will need to pay a performance penalty
|
157
177
|
def lazy_select_elements(&block)
|
158
178
|
@elements.select(&block).to_enum # non-lazy evaluation
|
159
179
|
end
|
data/lib/capybara/rspec.rb
CHANGED
@@ -9,6 +9,8 @@ require 'capybara/rspec/matcher_proxies'
|
|
9
9
|
RSpec.configure do |config|
|
10
10
|
config.include Capybara::DSL, type: :feature
|
11
11
|
config.include Capybara::RSpecMatchers, type: :feature
|
12
|
+
config.include Capybara::DSL, type: :system
|
13
|
+
config.include Capybara::RSpecMatchers, type: :system
|
12
14
|
config.include Capybara::RSpecMatchers, type: :view
|
13
15
|
|
14
16
|
# The before and after blocks must run instantaneously, because Capybara
|
@@ -2,17 +2,17 @@
|
|
2
2
|
|
3
3
|
module Capybara
|
4
4
|
module RSpecMatcherProxies
|
5
|
-
def all(*args, &block)
|
5
|
+
def all(*args, **kwargs, &block)
|
6
6
|
if defined?(::RSpec::Matchers::BuiltIn::All) && args.first.respond_to?(:matches?)
|
7
7
|
::RSpec::Matchers::BuiltIn::All.new(*args)
|
8
8
|
else
|
9
|
-
find_all(*args, &block)
|
9
|
+
find_all(*args, **kwargs, &block)
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
def within(*args, &block)
|
14
|
-
if
|
15
|
-
within_element(*args, &block)
|
13
|
+
def within(*args, **kwargs, &block)
|
14
|
+
if block
|
15
|
+
within_element(*args, **kwargs, &block)
|
16
16
|
else
|
17
17
|
be_within(*args)
|
18
18
|
end
|
@@ -15,45 +15,45 @@ module Capybara
|
|
15
15
|
# RSpec matcher for whether the element(s) matching a given selector exist.
|
16
16
|
#
|
17
17
|
# @see Capybara::Node::Matchers#assert_selector
|
18
|
-
def have_selector(*args, &optional_filter_block)
|
19
|
-
Matchers::HaveSelector.new(*args, &optional_filter_block)
|
18
|
+
def have_selector(*args, **kw_args, &optional_filter_block)
|
19
|
+
Matchers::HaveSelector.new(*args, **kw_args, &optional_filter_block)
|
20
20
|
end
|
21
21
|
|
22
22
|
# RSpec matcher for whether the element(s) matching a group of selectors exist.
|
23
23
|
#
|
24
24
|
# @see Capybara::Node::Matchers#assert_all_of_selectors
|
25
|
-
def have_all_of_selectors(*args, &optional_filter_block)
|
26
|
-
Matchers::HaveAllSelectors.new(*args, &optional_filter_block)
|
25
|
+
def have_all_of_selectors(*args, **kw_args, &optional_filter_block)
|
26
|
+
Matchers::HaveAllSelectors.new(*args, **kw_args, &optional_filter_block)
|
27
27
|
end
|
28
28
|
|
29
29
|
# RSpec matcher for whether no element(s) matching a group of selectors exist.
|
30
30
|
#
|
31
31
|
# @see Capybara::Node::Matchers#assert_none_of_selectors
|
32
|
-
def have_none_of_selectors(*args, &optional_filter_block)
|
33
|
-
Matchers::HaveNoSelectors.new(*args, &optional_filter_block)
|
32
|
+
def have_none_of_selectors(*args, **kw_args, &optional_filter_block)
|
33
|
+
Matchers::HaveNoSelectors.new(*args, **kw_args, &optional_filter_block)
|
34
34
|
end
|
35
35
|
|
36
36
|
# RSpec matcher for whether the element(s) matching any of a group of selectors exist.
|
37
37
|
#
|
38
38
|
# @see Capybara::Node::Matchers#assert_any_of_selectors
|
39
|
-
def have_any_of_selectors(*args, &optional_filter_block)
|
40
|
-
Matchers::HaveAnySelectors.new(*args, &optional_filter_block)
|
39
|
+
def have_any_of_selectors(*args, **kw_args, &optional_filter_block)
|
40
|
+
Matchers::HaveAnySelectors.new(*args, **kw_args, &optional_filter_block)
|
41
41
|
end
|
42
42
|
|
43
43
|
# RSpec matcher for whether the current element matches a given selector.
|
44
44
|
#
|
45
45
|
# @see Capybara::Node::Matchers#assert_matches_selector
|
46
|
-
def match_selector(*args, &optional_filter_block)
|
47
|
-
Matchers::MatchSelector.new(*args, &optional_filter_block)
|
46
|
+
def match_selector(*args, **kw_args, &optional_filter_block)
|
47
|
+
Matchers::MatchSelector.new(*args, **kw_args, &optional_filter_block)
|
48
48
|
end
|
49
49
|
|
50
50
|
%i[css xpath].each do |selector|
|
51
51
|
define_method "have_#{selector}" do |expr, **options, &optional_filter_block|
|
52
|
-
Matchers::HaveSelector.new(selector, expr, options, &optional_filter_block)
|
52
|
+
Matchers::HaveSelector.new(selector, expr, **options, &optional_filter_block)
|
53
53
|
end
|
54
54
|
|
55
55
|
define_method "match_#{selector}" do |expr, **options, &optional_filter_block|
|
56
|
-
Matchers::MatchSelector.new(selector, expr, options, &optional_filter_block)
|
56
|
+
Matchers::MatchSelector.new(selector, expr, **options, &optional_filter_block)
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
@@ -79,7 +79,7 @@ module Capybara
|
|
79
79
|
|
80
80
|
%i[link button field select table].each do |selector|
|
81
81
|
define_method "have_#{selector}" do |locator = nil, **options, &optional_filter_block|
|
82
|
-
Matchers::HaveSelector.new(selector, locator, options, &optional_filter_block)
|
82
|
+
Matchers::HaveSelector.new(selector, locator, **options, &optional_filter_block)
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
@@ -94,7 +94,7 @@ module Capybara
|
|
94
94
|
# @see Capybara::Node::Matchers#has_button?
|
95
95
|
|
96
96
|
# @!method have_field(locator = nil, **options, &optional_filter_block)
|
97
|
-
# RSpec matcher for
|
97
|
+
# RSpec matcher for form fields.
|
98
98
|
#
|
99
99
|
# @see Capybara::Node::Matchers#has_field?
|
100
100
|
|
@@ -110,7 +110,7 @@ module Capybara
|
|
110
110
|
|
111
111
|
%i[checked unchecked].each do |state|
|
112
112
|
define_method "have_#{state}_field" do |locator = nil, **options, &optional_filter_block|
|
113
|
-
Matchers::HaveSelector.new(:field, locator, options.merge(state => true), &optional_filter_block)
|
113
|
+
Matchers::HaveSelector.new(:field, locator, **options.merge(state => true), &optional_filter_block)
|
114
114
|
end
|
115
115
|
end
|
116
116
|
|
@@ -127,64 +127,65 @@ module Capybara
|
|
127
127
|
# RSpec matcher for text content.
|
128
128
|
#
|
129
129
|
# @see Capybara::Node::Matchers#assert_text
|
130
|
-
def have_text(*args)
|
131
|
-
Matchers::HaveText.new(*args)
|
130
|
+
def have_text(text_or_type, *args, **options)
|
131
|
+
Matchers::HaveText.new(text_or_type, *args, **options)
|
132
132
|
end
|
133
133
|
alias_method :have_content, :have_text
|
134
134
|
|
135
135
|
def have_title(title, **options)
|
136
|
-
Matchers::HaveTitle.new(title, options)
|
136
|
+
Matchers::HaveTitle.new(title, **options)
|
137
137
|
end
|
138
138
|
|
139
139
|
# RSpec matcher for the current path.
|
140
140
|
#
|
141
141
|
# @see Capybara::SessionMatchers#assert_current_path
|
142
|
-
def have_current_path(path, **options)
|
143
|
-
Matchers::HaveCurrentPath.new(path, options)
|
142
|
+
def have_current_path(path, **options, &optional_filter_block)
|
143
|
+
Matchers::HaveCurrentPath.new(path, **options, &optional_filter_block)
|
144
144
|
end
|
145
145
|
|
146
146
|
# RSpec matcher for element style.
|
147
147
|
#
|
148
148
|
# @see Capybara::Node::Matchers#matches_style?
|
149
|
-
def match_style(styles, **options)
|
150
|
-
|
149
|
+
def match_style(styles = nil, **options)
|
150
|
+
styles, options = options, {} if styles.nil?
|
151
|
+
Matchers::MatchStyle.new(styles, **options)
|
151
152
|
end
|
152
153
|
|
153
154
|
##
|
154
155
|
# @deprecated
|
155
156
|
#
|
156
|
-
def have_style(styles, **options)
|
157
|
-
warn
|
157
|
+
def have_style(styles = nil, **options)
|
158
|
+
Capybara::Helpers.warn "DEPRECATED: have_style is deprecated, please use match_style : #{Capybara::Helpers.filter_backtrace(caller)}"
|
158
159
|
match_style(styles, **options)
|
159
160
|
end
|
160
161
|
|
161
162
|
%w[selector css xpath text title current_path link button
|
162
163
|
field checked_field unchecked_field select table
|
163
164
|
sibling ancestor].each do |matcher_type|
|
164
|
-
define_method "have_no_#{matcher_type}" do |*args, &optional_filter_block|
|
165
|
-
Matchers::NegatedMatcher.new(send("have_#{matcher_type}", *args, &optional_filter_block))
|
165
|
+
define_method "have_no_#{matcher_type}" do |*args, **kw_args, &optional_filter_block|
|
166
|
+
Matchers::NegatedMatcher.new(send("have_#{matcher_type}", *args, **kw_args, &optional_filter_block))
|
166
167
|
end
|
167
168
|
end
|
168
169
|
alias_method :have_no_content, :have_no_text
|
169
170
|
|
170
171
|
%w[selector css xpath].each do |matcher_type|
|
171
|
-
define_method "not_match_#{matcher_type}" do |*args, &optional_filter_block|
|
172
|
-
Matchers::NegatedMatcher.new(send("match_#{matcher_type}", *args, &optional_filter_block))
|
172
|
+
define_method "not_match_#{matcher_type}" do |*args, **kw_args, &optional_filter_block|
|
173
|
+
Matchers::NegatedMatcher.new(send("match_#{matcher_type}", *args, **kw_args, &optional_filter_block))
|
173
174
|
end
|
174
175
|
end
|
175
176
|
|
176
177
|
# RSpec matcher for whether sibling element(s) matching a given selector exist.
|
177
178
|
#
|
178
179
|
# @see Capybara::Node::Matchers#assert_sibling
|
179
|
-
def have_sibling(*args, &optional_filter_block)
|
180
|
-
Matchers::HaveSibling.new(*args, &optional_filter_block)
|
180
|
+
def have_sibling(*args, **kw_args, &optional_filter_block)
|
181
|
+
Matchers::HaveSibling.new(*args, **kw_args, &optional_filter_block)
|
181
182
|
end
|
182
183
|
|
183
184
|
# RSpec matcher for whether ancestor element(s) matching a given selector exist.
|
184
185
|
#
|
185
186
|
# @see Capybara::Node::Matchers#assert_ancestor
|
186
|
-
def have_ancestor(*args, &optional_filter_block)
|
187
|
-
Matchers::HaveAncestor.new(*args, &optional_filter_block)
|
187
|
+
def have_ancestor(*args, **kw_args, &optional_filter_block)
|
188
|
+
Matchers::HaveAncestor.new(*args, **kw_args, &optional_filter_block)
|
188
189
|
end
|
189
190
|
|
190
191
|
##
|