capybara 3.33.0 → 3.34.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 +44 -15
- data/README.md +0 -2
- data/lib/capybara.rb +1 -1
- data/lib/capybara/config.rb +4 -6
- data/lib/capybara/driver/base.rb +4 -0
- data/lib/capybara/helpers.rb +25 -1
- data/lib/capybara/minitest.rb +2 -2
- data/lib/capybara/minitest/spec.rb +14 -11
- data/lib/capybara/node/actions.rb +1 -2
- data/lib/capybara/node/element.rb +1 -5
- data/lib/capybara/node/finders.rb +7 -6
- data/lib/capybara/node/matchers.rb +7 -5
- data/lib/capybara/node/simple.rb +5 -1
- data/lib/capybara/queries/ancestor_query.rb +1 -1
- data/lib/capybara/queries/current_path_query.rb +14 -4
- data/lib/capybara/queries/selector_query.rb +8 -9
- data/lib/capybara/queries/sibling_query.rb +1 -1
- data/lib/capybara/queries/text_query.rb +2 -2
- data/lib/capybara/rack_test/browser.rb +7 -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 +1 -1
- data/lib/capybara/registration_container.rb +3 -3
- data/lib/capybara/registrations/patches/puma_ssl.rb +3 -1
- data/lib/capybara/registrations/servers.rb +1 -0
- data/lib/capybara/result.rb +3 -7
- data/lib/capybara/rspec.rb +2 -0
- data/lib/capybara/rspec/matcher_proxies.rb +1 -1
- data/lib/capybara/rspec/matchers.rb +7 -6
- data/lib/capybara/rspec/matchers/have_current_path.rb +2 -2
- data/lib/capybara/rspec/matchers/match_style.rb +5 -0
- data/lib/capybara/selector/definition.rb +6 -5
- data/lib/capybara/selector/definition/button.rb +7 -5
- 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/label.rb +1 -1
- data/lib/capybara/selector/definition/select.rb +1 -1
- data/lib/capybara/selector/definition/table_row.rb +1 -1
- data/lib/capybara/selector/filter_set.rb +2 -2
- data/lib/capybara/selector/selector.rb +5 -1
- data/lib/capybara/selenium/driver.rb +29 -3
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +3 -3
- data/lib/capybara/selenium/driver_specializations/edge_driver.rb +3 -3
- data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +1 -1
- data/lib/capybara/selenium/extensions/find.rb +3 -3
- data/lib/capybara/selenium/extensions/scroll.rb +8 -10
- data/lib/capybara/selenium/node.rb +6 -3
- data/lib/capybara/selenium/nodes/chrome_node.rb +20 -2
- data/lib/capybara/selenium/nodes/safari_node.rb +1 -1
- data/lib/capybara/selenium/patches/atoms.rb +4 -4
- data/lib/capybara/selenium/patches/logs.rb +4 -4
- data/lib/capybara/server/animation_disabler.rb +3 -2
- data/lib/capybara/server/middleware.rb +4 -2
- data/lib/capybara/session.rb +20 -11
- data/lib/capybara/session/matchers.rb +11 -11
- data/lib/capybara/spec/public/test.js +6 -1
- data/lib/capybara/spec/session/accept_alert_spec.rb +1 -1
- data/lib/capybara/spec/session/check_spec.rb +6 -0
- data/lib/capybara/spec/session/current_url_spec.rb +11 -1
- data/lib/capybara/spec/session/has_button_spec.rb +2 -0
- data/lib/capybara/spec/session/has_css_spec.rb +2 -1
- data/lib/capybara/spec/session/has_current_path_spec.rb +13 -0
- data/lib/capybara/spec/session/has_text_spec.rb +0 -11
- data/lib/capybara/spec/session/matches_style_spec.rb +2 -2
- data/lib/capybara/spec/session/node_spec.rb +22 -2
- data/lib/capybara/spec/session/refresh_spec.rb +1 -0
- data/lib/capybara/spec/session/save_page_spec.rb +4 -4
- data/lib/capybara/spec/spec_helper.rb +11 -12
- data/lib/capybara/spec/test_app.rb +8 -3
- data/lib/capybara/spec/views/form.erb +18 -0
- data/lib/capybara/spec/views/with_animation.erb +8 -0
- data/lib/capybara/spec/views/with_js.erb +1 -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/dsl_spec.rb +1 -1
- data/spec/fixtures/selenium_driver_rspec_success.rb +1 -1
- data/spec/minitest_spec.rb +2 -1
- data/spec/rack_test_spec.rb +15 -5
- data/spec/rspec/features_spec.rb +3 -1
- data/spec/rspec/scenarios_spec.rb +4 -0
- data/spec/rspec/shared_spec_matchers.rb +2 -2
- data/spec/rspec_spec.rb +4 -0
- data/spec/selector_spec.rb +1 -0
- data/spec/server_spec.rb +15 -0
- data/spec/shared_selenium_session.rb +60 -1
- metadata +22 -23
- data/lib/capybara/spec/session/source_spec.rb +0 -0
@@ -18,7 +18,8 @@ Capybara.add_selector(:element, locator_type: [String, Symbol]) do
|
|
18
18
|
end
|
19
19
|
|
20
20
|
describe_expression_filters do |**options|
|
21
|
-
|
21
|
+
boolean_values = [true, false]
|
22
|
+
booleans, values = options.partition { |_k, v| boolean_values.include? v }.map(&:to_h)
|
22
23
|
desc = describe_all_expression_filters(**values)
|
23
24
|
desc + booleans.map do |k, v|
|
24
25
|
v ? " with #{k} attribute" : "without #{k} attribute"
|
@@ -53,7 +53,7 @@ Capybara.add_selector(:label, locator_type: [String, Symbol]) do
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
describe_node_filters do |**options|
|
56
|
-
" for element #{options[:for]}" if options[:for]
|
56
|
+
" for element #{options[:for]}" if options[:for].is_a?(Capybara::Node::Element)
|
57
57
|
end
|
58
58
|
|
59
59
|
def labelable_elements
|
@@ -33,7 +33,7 @@ Capybara.add_selector(:select, locator_type: [String, Symbol]) do
|
|
33
33
|
|
34
34
|
expression_filter(:with_options) do |expr, options|
|
35
35
|
options.inject(expr) do |xpath, option|
|
36
|
-
xpath
|
36
|
+
xpath.where(expression_for(:option, option))
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
@@ -14,7 +14,7 @@ Capybara.add_selector(:table_row, locator_type: [Array, Hash]) do
|
|
14
14
|
else
|
15
15
|
initial_td = XPath.descendant(:td)[XPath.string.n.is(locator.shift)]
|
16
16
|
tds = locator.reverse.map { |cell| XPath.following_sibling(:td)[XPath.string.n.is(cell)] }
|
17
|
-
.reduce { |xp, cell| xp
|
17
|
+
.reduce { |xp, cell| xp.where(cell) }
|
18
18
|
xpath[initial_td[tds]]
|
19
19
|
end
|
20
20
|
end
|
@@ -12,7 +12,7 @@ module Capybara
|
|
12
12
|
@node_filters = {}
|
13
13
|
@expression_filters = {}
|
14
14
|
@descriptions = Hash.new { |hsh, key| hsh[key] = [] }
|
15
|
-
instance_eval(&block)
|
15
|
+
instance_eval(&block) if block
|
16
16
|
end
|
17
17
|
|
18
18
|
def node_filter(names, *types, **options, &block)
|
@@ -49,7 +49,7 @@ module Capybara
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def descriptions
|
52
|
-
warn 'DEPRECATED: FilterSet#descriptions is deprecated without replacement'
|
52
|
+
Capybara::Helpers.warn 'DEPRECATED: FilterSet#descriptions is deprecated without replacement'
|
53
53
|
[undeclared_descriptions, node_filter_descriptions, expression_filter_descriptions].flatten
|
54
54
|
end
|
55
55
|
|
@@ -132,7 +132,11 @@ module Capybara
|
|
132
132
|
attr_matchers |= XPath.attr(test_id) == locator if test_id
|
133
133
|
|
134
134
|
locate_xpath = locate_xpath[attr_matchers]
|
135
|
-
locate_xpath +
|
135
|
+
locate_xpath + locate_label(locator).descendant(xpath)
|
136
|
+
end
|
137
|
+
|
138
|
+
def locate_label(locator)
|
139
|
+
XPath.descendant(:label)[XPath.string.n.is(locator)]
|
136
140
|
end
|
137
141
|
|
138
142
|
def find_by_attr(attribute, value)
|
@@ -21,11 +21,28 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
21
21
|
require 'capybara/selenium/patches/atoms'
|
22
22
|
require 'capybara/selenium/patches/is_displayed'
|
23
23
|
require 'capybara/selenium/patches/action_pauser'
|
24
|
-
|
24
|
+
|
25
|
+
# Look up the version of `selenium-webdriver` to
|
26
|
+
# see if it's a version we support.
|
27
|
+
#
|
28
|
+
# By default, we use Gem.loaded_specs to determine
|
29
|
+
# the version number. However, in some cases, such
|
30
|
+
# as when loading `selenium-webdriver` outside of
|
31
|
+
# Rubygems, we fall back to referencing
|
32
|
+
# Selenium::WebDriver::VERSION. Ideally we'd
|
33
|
+
# use the constant in all cases, but earlier versions
|
34
|
+
# of `selenium-webdriver` didn't provide the constant.
|
35
|
+
selenium_webdriver_version =
|
36
|
+
if Gem.loaded_specs['selenium-webdriver']
|
37
|
+
Gem.loaded_specs['selenium-webdriver'].version
|
38
|
+
else
|
39
|
+
Gem::Version.new(Selenium::WebDriver::VERSION)
|
40
|
+
end
|
41
|
+
if selenium_webdriver_version < Gem::Version.new('3.5.0')
|
25
42
|
warn "Warning: You're using an unsupported version of selenium-webdriver, please upgrade."
|
26
43
|
end
|
27
44
|
rescue LoadError => e
|
28
|
-
raise e unless e.message.
|
45
|
+
raise e unless e.message.include?('selenium-webdriver')
|
29
46
|
|
30
47
|
raise LoadError, "Capybara's selenium driver is unable to load `selenium-webdriver`, please install the gem and add `gem 'selenium-webdriver'` to your Gemfile if you are using bundler."
|
31
48
|
end
|
@@ -58,6 +75,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
58
75
|
end
|
59
76
|
|
60
77
|
def initialize(app, **options)
|
78
|
+
super()
|
61
79
|
self.class.load_selenium
|
62
80
|
@app = app
|
63
81
|
@browser = nil
|
@@ -86,7 +104,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
86
104
|
def html
|
87
105
|
browser.page_source
|
88
106
|
rescue Selenium::WebDriver::Error::JavascriptError => e
|
89
|
-
raise unless e.message.
|
107
|
+
raise unless e.message.include?('documentElement is null')
|
90
108
|
end
|
91
109
|
|
92
110
|
def title
|
@@ -115,6 +133,10 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
115
133
|
unwrap_script_result(result)
|
116
134
|
end
|
117
135
|
|
136
|
+
def send_keys(*args)
|
137
|
+
active_element.send_keys(*args)
|
138
|
+
end
|
139
|
+
|
118
140
|
def save_screenshot(path, **_options)
|
119
141
|
browser.save_screenshot(path)
|
120
142
|
end
|
@@ -437,6 +459,10 @@ private
|
|
437
459
|
browser
|
438
460
|
end
|
439
461
|
|
462
|
+
def active_element
|
463
|
+
browser.switch_to.active_element
|
464
|
+
end
|
465
|
+
|
440
466
|
def build_node(native_node, initial_cache = {})
|
441
467
|
::Capybara::Selenium::Node.new(self, native_node, initial_cache)
|
442
468
|
end
|
@@ -7,7 +7,7 @@ module Capybara::Selenium::Driver::ChromeDriver
|
|
7
7
|
def self.extended(base)
|
8
8
|
bridge = base.send(:bridge)
|
9
9
|
bridge.extend Capybara::Selenium::ChromeLogs unless bridge.respond_to?(:log)
|
10
|
-
bridge.extend Capybara::Selenium::IsDisplayed unless bridge.
|
10
|
+
bridge.extend Capybara::Selenium::IsDisplayed unless bridge.send(:commands, :is_element_displayed)
|
11
11
|
base.options[:native_displayed] = false if base.options[:native_displayed].nil?
|
12
12
|
end
|
13
13
|
|
@@ -15,7 +15,7 @@ module Capybara::Selenium::Driver::ChromeDriver
|
|
15
15
|
within_given_window(handle) do
|
16
16
|
super
|
17
17
|
rescue NoMethodError => e
|
18
|
-
raise unless e.message.
|
18
|
+
raise unless e.message.include?('full_screen_window')
|
19
19
|
|
20
20
|
result = bridge.http.call(:post, "session/#{bridge.session_id}/window/fullscreen", {})
|
21
21
|
result['value']
|
@@ -25,7 +25,7 @@ module Capybara::Selenium::Driver::ChromeDriver
|
|
25
25
|
def resize_window_to(handle, width, height)
|
26
26
|
super
|
27
27
|
rescue Selenium::WebDriver::Error::UnknownError => e
|
28
|
-
raise unless e.message.
|
28
|
+
raise unless e.message.include?('failed to change window state')
|
29
29
|
|
30
30
|
# Chromedriver doesn't wait long enough for state to change when coming out of fullscreen
|
31
31
|
# and raises unnecessary error. Wait a bit and try again.
|
@@ -5,7 +5,7 @@ require 'capybara/selenium/nodes/edge_node'
|
|
5
5
|
module Capybara::Selenium::Driver::EdgeDriver
|
6
6
|
def self.extended(base)
|
7
7
|
bridge = base.send(:bridge)
|
8
|
-
bridge.extend Capybara::Selenium::IsDisplayed unless bridge.
|
8
|
+
bridge.extend Capybara::Selenium::IsDisplayed unless bridge.send(:commands, :is_element_displayed)
|
9
9
|
base.options[:native_displayed] = false if base.options[:native_displayed].nil?
|
10
10
|
end
|
11
11
|
|
@@ -15,7 +15,7 @@ module Capybara::Selenium::Driver::EdgeDriver
|
|
15
15
|
within_given_window(handle) do
|
16
16
|
super
|
17
17
|
rescue NoMethodError => e
|
18
|
-
raise unless e.message.
|
18
|
+
raise unless e.message.include?('full_screen_window')
|
19
19
|
|
20
20
|
result = bridge.http.call(:post, "session/#{bridge.session_id}/window/fullscreen", {})
|
21
21
|
result['value']
|
@@ -25,7 +25,7 @@ module Capybara::Selenium::Driver::EdgeDriver
|
|
25
25
|
def resize_window_to(handle, width, height)
|
26
26
|
super
|
27
27
|
rescue Selenium::WebDriver::Error::UnknownError => e
|
28
|
-
raise unless e.message.
|
28
|
+
raise unless e.message.include?('failed to change window state')
|
29
29
|
|
30
30
|
# Chromedriver doesn't wait long enough for state to change when coming out of fullscreen
|
31
31
|
# and raises unnecessary error. Wait a bit and try again.
|
@@ -6,7 +6,7 @@ module Capybara::Selenium::Driver::FirefoxDriver
|
|
6
6
|
def self.extended(driver)
|
7
7
|
driver.extend Capybara::Selenium::Driver::W3CFirefoxDriver if w3c?(driver)
|
8
8
|
bridge = driver.send(:bridge)
|
9
|
-
bridge.extend Capybara::Selenium::IsDisplayed unless bridge.
|
9
|
+
bridge.extend Capybara::Selenium::IsDisplayed unless bridge.send(:commands, :is_element_displayed)
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.w3c?(driver)
|
@@ -100,9 +100,9 @@ module Capybara
|
|
100
100
|
def is_displayed_atom # rubocop:disable Naming/PredicateName
|
101
101
|
@@is_displayed_atom ||= begin # rubocop:disable Style/ClassVars
|
102
102
|
browser.send(:bridge).send(:read_atom, 'isDisplayed')
|
103
|
-
|
104
|
-
|
105
|
-
|
103
|
+
rescue StandardError
|
104
|
+
# If the atom doesn't exist or other error
|
105
|
+
''
|
106
106
|
end
|
107
107
|
end
|
108
108
|
end
|
@@ -45,20 +45,18 @@ module Capybara
|
|
45
45
|
JS
|
46
46
|
end
|
47
47
|
|
48
|
+
SCROLL_POSITIONS = {
|
49
|
+
top: '0',
|
50
|
+
bottom: 'arguments[0].scrollHeight',
|
51
|
+
center: '(arguments[0].scrollHeight - arguments[0].clientHeight)/2'
|
52
|
+
}.freeze
|
53
|
+
|
48
54
|
def scroll_to_location(location)
|
49
|
-
scroll_y = case location
|
50
|
-
when :top
|
51
|
-
'0'
|
52
|
-
when :bottom
|
53
|
-
'arguments[0].scrollHeight'
|
54
|
-
when :center
|
55
|
-
'(arguments[0].scrollHeight - arguments[0].clientHeight)/2'
|
56
|
-
end
|
57
55
|
driver.execute_script <<~JS, self
|
58
56
|
if (arguments[0].scrollTo){
|
59
|
-
arguments[0].scrollTo(0, #{
|
57
|
+
arguments[0].scrollTo(0, #{SCROLL_POSITIONS[location]});
|
60
58
|
} else {
|
61
|
-
arguments[0].scrollTop = #{
|
59
|
+
arguments[0].scrollTop = #{SCROLL_POSITIONS[location]};
|
62
60
|
}
|
63
61
|
JS
|
64
62
|
end
|
@@ -120,7 +120,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
120
120
|
end
|
121
121
|
rescue StandardError => e
|
122
122
|
if e.is_a?(::Selenium::WebDriver::Error::ElementClickInterceptedError) ||
|
123
|
-
e.message.
|
123
|
+
e.message.include?('Other element would receive the click')
|
124
124
|
scroll_to_center
|
125
125
|
end
|
126
126
|
|
@@ -464,8 +464,8 @@ private
|
|
464
464
|
end
|
465
465
|
end
|
466
466
|
|
467
|
-
def each_key(keys)
|
468
|
-
normalize_keys(keys).each
|
467
|
+
def each_key(keys, &block)
|
468
|
+
normalize_keys(keys).each(&block)
|
469
469
|
end
|
470
470
|
|
471
471
|
def find_context
|
@@ -493,6 +493,9 @@ private
|
|
493
493
|
var xpath = '';
|
494
494
|
var pos, tempitem2;
|
495
495
|
|
496
|
+
if (el.getRootNode && el.getRootNode() instanceof ShadowRoot) {
|
497
|
+
return "(: Shadow DOM element - no XPath :)";
|
498
|
+
};
|
496
499
|
while(el !== xml.documentElement) {
|
497
500
|
pos = 0;
|
498
501
|
tempitem2 = el;
|
@@ -42,7 +42,7 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
|
|
42
42
|
raise
|
43
43
|
rescue ::Selenium::WebDriver::Error::WebDriverError => e
|
44
44
|
# chromedriver 74 (at least on mac) raises the wrong error for this
|
45
|
-
if e.message.
|
45
|
+
if e.message.include?('element click intercepted')
|
46
46
|
raise ::Selenium::WebDriver::Error::ElementClickInterceptedError, e.message
|
47
47
|
end
|
48
48
|
|
@@ -73,6 +73,24 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
+
def send_keys(*args)
|
77
|
+
args.chunk { |inp| inp.is_a?(String) && inp.match?(/\p{Emoji Presentation}/) }
|
78
|
+
.each do |contains_emoji, inputs|
|
79
|
+
if contains_emoji
|
80
|
+
inputs.join.grapheme_clusters.chunk { |gc| gc.match?(/\p{Emoji Presentation}/) }
|
81
|
+
.each do |emoji, clusters|
|
82
|
+
if emoji
|
83
|
+
driver.send(:execute_cdp, 'Input.insertText', text: clusters.join)
|
84
|
+
else
|
85
|
+
super(clusters.join)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
else
|
89
|
+
super(*inputs)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
76
94
|
private
|
77
95
|
|
78
96
|
def perform_legacy_drag(element, drop_modifiers)
|
@@ -92,7 +110,7 @@ private
|
|
92
110
|
end
|
93
111
|
end
|
94
112
|
|
95
|
-
def browser_version(to_float
|
113
|
+
def browser_version(to_float: true)
|
96
114
|
caps = capabilities
|
97
115
|
ver = (caps[:browser_version] || caps[:version])
|
98
116
|
ver = ver.to_f if to_float
|
@@ -43,7 +43,7 @@ class Capybara::Selenium::SafariNode < Capybara::Selenium::Node
|
|
43
43
|
return '' unless visible?
|
44
44
|
|
45
45
|
vis_text = driver.execute_script('return arguments[0].innerText', self)
|
46
|
-
vis_text.
|
46
|
+
vis_text.squeeze(' ')
|
47
47
|
.gsub(/[\ \n]*\n[\ \n]*/, "\n")
|
48
48
|
.gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
|
49
49
|
.gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
|
@@ -6,10 +6,10 @@ private
|
|
6
6
|
def read_atom(function)
|
7
7
|
@atoms ||= Hash.new do |hash, key|
|
8
8
|
hash[key] = begin
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
File.read(File.expand_path("../../atoms/#{key}.min.js", __FILE__))
|
10
|
+
rescue Errno::ENOENT
|
11
|
+
super
|
12
|
+
end
|
13
13
|
end
|
14
14
|
@atoms[function]
|
15
15
|
end
|
@@ -27,10 +27,10 @@ module Capybara
|
|
27
27
|
|
28
28
|
def log(type)
|
29
29
|
data = begin
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
execute :get_log, {}, type: type.to_s
|
31
|
+
rescue ::Selenium::WebDriver::Error::UnknownCommandError
|
32
|
+
execute :get_log_legacy, {}, type: type.to_s
|
33
|
+
end
|
34
34
|
|
35
35
|
Array(data).map do |l|
|
36
36
|
::Selenium::WebDriver::LogEntry.new l.fetch('level', 'UNKNOWN'), l.fetch('timestamp'), l.fetch('message')
|
@@ -40,16 +40,17 @@ module Capybara
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def insert_disable(html)
|
43
|
-
html.sub(%r{(</head>)}, disable_markup
|
43
|
+
html.sub(%r{(</head>)}, "#{disable_markup}\\1")
|
44
44
|
end
|
45
45
|
|
46
46
|
DISABLE_MARKUP_TEMPLATE = <<~HTML
|
47
47
|
<script defer>(typeof jQuery !== 'undefined') && (jQuery.fx.off = true);</script>
|
48
48
|
<style>
|
49
|
-
|
49
|
+
%<selector>s, %<selector>s::before, %<selector>s::after {
|
50
50
|
transition: none !important;
|
51
51
|
animation-duration: 0s !important;
|
52
52
|
animation-delay: 0s !important;
|
53
|
+
scroll-behavior: auto !important;
|
53
54
|
}
|
54
55
|
</style>
|
55
56
|
HTML
|
@@ -53,14 +53,16 @@ module Capybara
|
|
53
53
|
if env['PATH_INFO'] == '/__identify__'
|
54
54
|
[200, {}, [@app.object_id.to_s]]
|
55
55
|
else
|
56
|
-
|
56
|
+
request_uri = env['REQUEST_URI']
|
57
|
+
@counter.increment(request_uri)
|
58
|
+
|
57
59
|
begin
|
58
60
|
@extended_app.call(env)
|
59
61
|
rescue *@server_errors => e
|
60
62
|
@error ||= e
|
61
63
|
raise e
|
62
64
|
ensure
|
63
|
-
@counter.decrement(
|
65
|
+
@counter.decrement(request_uri)
|
64
66
|
end
|
65
67
|
end
|
66
68
|
end
|
data/lib/capybara/session.rb
CHANGED
@@ -58,7 +58,7 @@ module Capybara
|
|
58
58
|
].freeze
|
59
59
|
SESSION_METHODS = %i[
|
60
60
|
body html source current_url current_host current_path
|
61
|
-
execute_script evaluate_script visit refresh go_back go_forward
|
61
|
+
execute_script evaluate_script visit refresh go_back go_forward send_keys
|
62
62
|
within within_element within_fieldset within_table within_frame switch_to_frame
|
63
63
|
current_window windows open_new_window switch_to_window within_window window_opened_by
|
64
64
|
save_page save_and_open_page save_screenshot
|
@@ -303,6 +303,14 @@ module Capybara
|
|
303
303
|
driver.go_forward
|
304
304
|
end
|
305
305
|
|
306
|
+
##
|
307
|
+
# @!method send_keys
|
308
|
+
# @see Capybara::Node::Element#send_keys
|
309
|
+
#
|
310
|
+
def send_keys(*args, **kw_args)
|
311
|
+
driver.send_keys(*args, **kw_args)
|
312
|
+
end
|
313
|
+
|
306
314
|
##
|
307
315
|
#
|
308
316
|
# Executes the given block within the context of a node. {#within} takes the
|
@@ -355,8 +363,8 @@ module Capybara
|
|
355
363
|
#
|
356
364
|
# @param [String] locator Id or legend of the fieldset
|
357
365
|
#
|
358
|
-
def within_fieldset(locator)
|
359
|
-
within(:fieldset, locator)
|
366
|
+
def within_fieldset(locator, &block)
|
367
|
+
within(:fieldset, locator, &block)
|
360
368
|
end
|
361
369
|
|
362
370
|
##
|
@@ -365,8 +373,8 @@ module Capybara
|
|
365
373
|
#
|
366
374
|
# @param [String] locator Id or caption of the table
|
367
375
|
#
|
368
|
-
def within_table(locator)
|
369
|
-
within(:table, locator)
|
376
|
+
def within_table(locator, &block)
|
377
|
+
within(:table, locator, &block)
|
370
378
|
end
|
371
379
|
|
372
380
|
##
|
@@ -398,8 +406,9 @@ module Capybara
|
|
398
406
|
driver.switch_to_frame(:parent)
|
399
407
|
when :top
|
400
408
|
idx = scopes.index(:frame)
|
409
|
+
top_level_scopes = [:frame, nil]
|
401
410
|
if idx
|
402
|
-
if scopes.slice(idx..-1).any? { |scope| !
|
411
|
+
if scopes.slice(idx..-1).any? { |scope| !top_level_scopes.include?(scope) }
|
403
412
|
raise Capybara::ScopeError, "`switch_to_frame(:top)` cannot be called from inside a descendant frame's "\
|
404
413
|
'`within` block.'
|
405
414
|
end
|
@@ -488,8 +497,8 @@ module Capybara
|
|
488
497
|
# @raise [ArgumentError] if both or neither arguments were provided
|
489
498
|
#
|
490
499
|
def switch_to_window(window = nil, **options, &window_locator)
|
491
|
-
raise ArgumentError, '`switch_to_window` can take either a block or a window, not both' if window &&
|
492
|
-
raise ArgumentError, '`switch_to_window`: either window or block should be provided' if !window && !
|
500
|
+
raise ArgumentError, '`switch_to_window` can take either a block or a window, not both' if window && window_locator
|
501
|
+
raise ArgumentError, '`switch_to_window`: either window or block should be provided' if !window && !window_locator
|
493
502
|
|
494
503
|
unless scopes.last.nil?
|
495
504
|
raise Capybara::ScopeError, '`switch_to_window` is not supposed to be invoked from '\
|
@@ -738,7 +747,7 @@ module Capybara
|
|
738
747
|
# @param [Hash] options a customizable set of options
|
739
748
|
#
|
740
749
|
def save_and_open_screenshot(path = nil, **options)
|
741
|
-
save_screenshot(path, **options).tap { |s_path| open_file(s_path) }
|
750
|
+
save_screenshot(path, **options).tap { |s_path| open_file(s_path) }
|
742
751
|
end
|
743
752
|
|
744
753
|
def document
|
@@ -788,7 +797,7 @@ module Capybara
|
|
788
797
|
#
|
789
798
|
# Yield a block using a specific maximum wait time.
|
790
799
|
#
|
791
|
-
def using_wait_time(seconds)
|
800
|
+
def using_wait_time(seconds, &block)
|
792
801
|
if Capybara.threadsafe
|
793
802
|
begin
|
794
803
|
previous_wait_time = config.default_max_wait_time
|
@@ -798,7 +807,7 @@ module Capybara
|
|
798
807
|
config.default_max_wait_time = previous_wait_time
|
799
808
|
end
|
800
809
|
else
|
801
|
-
Capybara.using_wait_time(seconds)
|
810
|
+
Capybara.using_wait_time(seconds, &block)
|
802
811
|
end
|
803
812
|
end
|
804
813
|
|