capybara 2.13.0 → 2.18.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 +5 -5
- data/History.md +218 -18
- data/README.md +54 -23
- data/lib/capybara/config.rb +132 -0
- data/lib/capybara/cucumber.rb +1 -0
- data/lib/capybara/driver/base.rb +14 -0
- data/lib/capybara/dsl.rb +1 -3
- data/lib/capybara/helpers.rb +3 -3
- data/lib/capybara/minitest/spec.rb +14 -37
- data/lib/capybara/minitest.rb +95 -114
- data/lib/capybara/node/actions.rb +10 -10
- data/lib/capybara/node/base.rb +7 -2
- data/lib/capybara/node/element.rb +9 -3
- data/lib/capybara/node/finders.rb +92 -18
- data/lib/capybara/node/matchers.rb +21 -9
- data/lib/capybara/node/simple.rb +5 -0
- data/lib/capybara/queries/ancestor_query.rb +25 -0
- data/lib/capybara/queries/base_query.rb +12 -3
- data/lib/capybara/queries/current_path_query.rb +13 -9
- data/lib/capybara/queries/selector_query.rb +62 -23
- data/lib/capybara/queries/sibling_query.rb +25 -0
- data/lib/capybara/queries/text_query.rb +10 -5
- data/lib/capybara/queries/title_query.rb +1 -0
- data/lib/capybara/rack_test/browser.rb +13 -5
- data/lib/capybara/rack_test/driver.rb +6 -1
- data/lib/capybara/rack_test/form.rb +4 -3
- data/lib/capybara/rack_test/node.rb +1 -1
- data/lib/capybara/rspec/compound.rb +95 -0
- data/lib/capybara/rspec/matcher_proxies.rb +45 -0
- data/lib/capybara/rspec/matchers.rb +108 -7
- data/lib/capybara/rspec.rb +3 -1
- data/lib/capybara/selector/filter.rb +13 -41
- data/lib/capybara/selector/filter_set.rb +30 -4
- data/lib/capybara/selector/filters/base.rb +33 -0
- data/lib/capybara/selector/filters/expression_filter.rb +40 -0
- data/lib/capybara/selector/filters/node_filter.rb +27 -0
- data/lib/capybara/selector/selector.rb +36 -15
- data/lib/capybara/selector.rb +63 -42
- data/lib/capybara/selenium/driver.rb +177 -33
- data/lib/capybara/selenium/node.rb +106 -55
- data/lib/capybara/server.rb +6 -5
- data/lib/capybara/session/config.rb +114 -0
- data/lib/capybara/session/matchers.rb +15 -4
- data/lib/capybara/session.rb +178 -65
- data/lib/capybara/spec/fixtures/no_extension +1 -0
- data/lib/capybara/spec/public/test.js +18 -3
- data/lib/capybara/spec/session/accept_alert_spec.rb +9 -1
- data/lib/capybara/spec/session/accept_prompt_spec.rb +29 -1
- data/lib/capybara/spec/session/all_spec.rb +13 -1
- data/lib/capybara/spec/session/ancestor_spec.rb +85 -0
- data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +24 -8
- data/lib/capybara/spec/session/assert_selector.rb +1 -1
- data/lib/capybara/spec/session/assert_text.rb +8 -0
- data/lib/capybara/spec/session/assert_title.rb +22 -9
- data/lib/capybara/spec/session/attach_file_spec.rb +8 -1
- data/lib/capybara/spec/session/check_spec.rb +4 -4
- data/lib/capybara/spec/session/choose_spec.rb +2 -2
- data/lib/capybara/spec/session/click_button_spec.rb +1 -1
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +3 -3
- data/lib/capybara/spec/session/click_link_spec.rb +1 -1
- data/lib/capybara/spec/session/current_url_spec.rb +3 -3
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +3 -3
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +1 -1
- data/lib/capybara/spec/session/evaluate_async_script_spec.rb +22 -0
- data/lib/capybara/spec/session/evaluate_script_spec.rb +1 -1
- data/lib/capybara/spec/session/fill_in_spec.rb +8 -2
- data/lib/capybara/spec/session/find_field_spec.rb +1 -0
- data/lib/capybara/spec/session/find_spec.rb +8 -6
- data/lib/capybara/spec/session/first_spec.rb +10 -5
- data/lib/capybara/spec/session/has_all_selectors_spec.rb +69 -0
- data/lib/capybara/spec/session/has_css_spec.rb +11 -0
- data/lib/capybara/spec/session/has_current_path_spec.rb +52 -7
- data/lib/capybara/spec/session/has_link_spec.rb +4 -4
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +76 -0
- data/lib/capybara/spec/session/has_select_spec.rb +64 -6
- data/lib/capybara/spec/session/has_selector_spec.rb +1 -3
- data/lib/capybara/spec/session/has_text_spec.rb +5 -3
- data/lib/capybara/spec/session/has_title_spec.rb +4 -2
- data/lib/capybara/spec/session/has_xpath_spec.rb +5 -3
- data/lib/capybara/spec/session/node_spec.rb +50 -26
- data/lib/capybara/spec/session/refresh_spec.rb +28 -0
- data/lib/capybara/spec/session/reset_session_spec.rb +3 -3
- data/lib/capybara/spec/session/select_spec.rb +3 -2
- data/lib/capybara/spec/session/sibling_spec.rb +52 -0
- data/lib/capybara/spec/session/uncheck_spec.rb +2 -2
- data/lib/capybara/spec/session/unselect_spec.rb +2 -2
- data/lib/capybara/spec/session/visit_spec.rb +56 -1
- data/lib/capybara/spec/session/window/become_closed_spec.rb +11 -11
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +11 -9
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +4 -4
- data/lib/capybara/spec/session/window/within_window_spec.rb +27 -2
- data/lib/capybara/spec/spec_helper.rb +28 -4
- data/lib/capybara/spec/test_app.rb +3 -1
- data/lib/capybara/spec/views/form.erb +27 -1
- data/lib/capybara/spec/views/initial_alert.erb +10 -0
- data/lib/capybara/spec/views/with_fixed_header_footer.erb +17 -0
- data/lib/capybara/spec/views/with_hover.erb +5 -0
- data/lib/capybara/spec/views/with_html.erb +33 -2
- data/lib/capybara/spec/views/with_js.erb +12 -0
- data/lib/capybara/spec/views/with_windows.erb +4 -0
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/window.rb +1 -1
- data/lib/capybara.rb +102 -124
- data/spec/capybara_spec.rb +43 -21
- data/spec/dsl_spec.rb +1 -0
- data/spec/filter_set_spec.rb +28 -0
- data/spec/minitest_spec.rb +9 -1
- data/spec/minitest_spec_spec.rb +19 -5
- data/spec/per_session_config_spec.rb +67 -0
- data/spec/result_spec.rb +20 -0
- data/spec/rspec/shared_spec_matchers.rb +148 -44
- data/spec/rspec/views_spec.rb +4 -0
- data/spec/rspec_matchers_spec.rb +46 -0
- data/spec/rspec_spec.rb +77 -0
- data/spec/selector_spec.rb +2 -1
- data/spec/selenium_spec_chrome.rb +25 -17
- data/spec/selenium_spec_firefox.rb +2 -1
- data/spec/selenium_spec_marionette.rb +18 -5
- data/spec/session_spec.rb +44 -0
- data/spec/shared_selenium_session.rb +72 -8
- data/spec/spec_helper.rb +4 -0
- metadata +55 -8
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'delegate'
|
3
|
+
|
4
|
+
module Capybara
|
5
|
+
class SessionConfig
|
6
|
+
OPTIONS = [:always_include_port, :run_server, :default_selector, :default_max_wait_time, :ignore_hidden_elements,
|
7
|
+
:automatic_reload, :match, :exact, :exact_text, :raise_server_errors, :visible_text_only, :wait_on_first_by_default,
|
8
|
+
:automatic_label_click, :enable_aria_label, :save_path, :exact_options, :asset_host, :default_host, :app_host,
|
9
|
+
:save_and_open_page_path, :server_host, :server_port, :server_errors]
|
10
|
+
|
11
|
+
attr_accessor(*OPTIONS)
|
12
|
+
|
13
|
+
##
|
14
|
+
#@!method always_include_port
|
15
|
+
# See {Capybara.configure}
|
16
|
+
#@!method run_server
|
17
|
+
# See {Capybara.configure}
|
18
|
+
#@!method default_selector
|
19
|
+
# See {Capybara.configure}
|
20
|
+
#@!method default_max_wait_time
|
21
|
+
# See {Capybara.configure}
|
22
|
+
#@!method ignore_hidden_elements
|
23
|
+
# See {Capybara.configure}
|
24
|
+
#@!method automatic_reload
|
25
|
+
# See {Capybara.configure}
|
26
|
+
#@!method match
|
27
|
+
# See {Capybara.configure}
|
28
|
+
#@!method exact
|
29
|
+
# See {Capybara.configure}
|
30
|
+
#@!method raise_server_errors
|
31
|
+
# See {Capybara.configure}
|
32
|
+
#@!method visible_text_only
|
33
|
+
# See {Capybara.configure}
|
34
|
+
#@!method wait_on_first_by_default
|
35
|
+
# See {Capybara.configure}
|
36
|
+
#@!method automatic_label_click
|
37
|
+
# See {Capybara.configure}
|
38
|
+
#@!method enable_aria_label
|
39
|
+
# See {Capybara.configure}
|
40
|
+
#@!method save_path
|
41
|
+
# See {Capybara.configure}
|
42
|
+
#@deprecated
|
43
|
+
#@!method exact_options
|
44
|
+
# See {Capybara.configure}
|
45
|
+
#@!method asset_host
|
46
|
+
# See {Capybara.configure}
|
47
|
+
#@!method default_host
|
48
|
+
# See {Capybara.configure}
|
49
|
+
#@!method app_host
|
50
|
+
# See {Capybara.configure}
|
51
|
+
#@!method save_and_open_page_path
|
52
|
+
# See {Capybara.configure}
|
53
|
+
#@!method server_host
|
54
|
+
# See {Capybara.configure}
|
55
|
+
#@!method server_port
|
56
|
+
# See {Capybara.configure}
|
57
|
+
#@!method server_errors
|
58
|
+
# See {Capybara.configure}
|
59
|
+
|
60
|
+
remove_method :server_host
|
61
|
+
|
62
|
+
##
|
63
|
+
#
|
64
|
+
# @return [String] The IP address bound by default server
|
65
|
+
#
|
66
|
+
def server_host
|
67
|
+
@server_host || '127.0.0.1'
|
68
|
+
end
|
69
|
+
|
70
|
+
remove_method :server_errors=
|
71
|
+
def server_errors=(errors)
|
72
|
+
(@server_errors ||= []).replace(errors.dup)
|
73
|
+
end
|
74
|
+
|
75
|
+
remove_method :app_host=
|
76
|
+
def app_host=(url)
|
77
|
+
raise ArgumentError.new("Capybara.app_host should be set to a url (http://www.example.com)") unless url.nil? || (url =~ URI::Parser.new.make_regexp)
|
78
|
+
@app_host = url
|
79
|
+
end
|
80
|
+
|
81
|
+
remove_method :default_host=
|
82
|
+
def default_host=(url)
|
83
|
+
raise ArgumentError.new("Capybara.default_host should be set to a url (http://www.example.com)") unless url.nil? || (url =~ URI::Parser.new.make_regexp)
|
84
|
+
@default_host = url
|
85
|
+
end
|
86
|
+
|
87
|
+
remove_method :save_and_open_page_path=
|
88
|
+
def save_and_open_page_path=(path)
|
89
|
+
warn "DEPRECATED: #save_and_open_page_path is deprecated, please use #save_path instead. \n"\
|
90
|
+
"Note: Behavior is slightly different with relative paths - see documentation" unless path.nil?
|
91
|
+
@save_and_open_page_path = path
|
92
|
+
end
|
93
|
+
|
94
|
+
remove_method :exact_options=
|
95
|
+
def exact_options=(opt)
|
96
|
+
@exact_options = opt
|
97
|
+
warn "DEPRECATED: #exact_options is deprecated, please scope your findes/actions and use the `:exact` "\
|
98
|
+
"option if similar functionality is needed."
|
99
|
+
end
|
100
|
+
|
101
|
+
def initialize_copy(other)
|
102
|
+
super
|
103
|
+
@server_errors = @server_errors.dup
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class ReadOnlySessionConfig < SimpleDelegator
|
108
|
+
SessionConfig::OPTIONS.each do |m|
|
109
|
+
define_method "#{m}=" do |val|
|
110
|
+
raise "Per session settings are only supported when Capybara.threadsafe == true"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -3,16 +3,18 @@ module Capybara
|
|
3
3
|
module SessionMatchers
|
4
4
|
##
|
5
5
|
# Asserts that the page has the given path.
|
6
|
-
# By default this will compare against the
|
6
|
+
# By default, if passed a full url this will compare against the full url,
|
7
|
+
# if passed a path only the path+query portion will be compared, if passed a regexp
|
8
|
+
# the comparison will depend on the :url option
|
7
9
|
#
|
8
10
|
# @!macro current_path_query_params
|
9
11
|
# @overload $0(string, options = {})
|
10
12
|
# @param string [String] The string that the current 'path' should equal
|
11
13
|
# @overload $0(regexp, options = {})
|
12
14
|
# @param regexp [Regexp] The regexp that the current 'path' should match to
|
13
|
-
# @option options [
|
14
|
-
# @option options [Boolean] :
|
15
|
-
# @option options [
|
15
|
+
# @option options [Boolean] :url (true if `string` ia a full url, otherwise false) Whether the compare should be done against the full current url or just the path
|
16
|
+
# @option options [Boolean] :ignore_query (false) Whether the query portion of the current url/path should be ignored
|
17
|
+
# @option options [Numeric] :wait (Capybara.default_max_wait_time) Maximum time that Capybara will wait for the current url/path to eq/match given string/regexp argument
|
16
18
|
# @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
|
17
19
|
# @return [true]
|
18
20
|
#
|
@@ -22,6 +24,9 @@ module Capybara
|
|
22
24
|
|
23
25
|
##
|
24
26
|
# Asserts that the page doesn't have the given path.
|
27
|
+
# By default, if passed a full url this will compare against the full url,
|
28
|
+
# if passed a path only the path+query portion will be compared, if passed a regexp
|
29
|
+
# the comparison will depend on the :url option
|
25
30
|
#
|
26
31
|
# @macro current_path_query_params
|
27
32
|
# @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
|
@@ -33,6 +38,9 @@ module Capybara
|
|
33
38
|
|
34
39
|
##
|
35
40
|
# Checks if the page has the given path.
|
41
|
+
# By default, if passed a full url this will compare against the full url,
|
42
|
+
# if passed a path only the path+query portion will be compared, if passed a regexp
|
43
|
+
# the comparison will depend on the :url option
|
36
44
|
#
|
37
45
|
# @macro current_path_query_params
|
38
46
|
# @return [Boolean]
|
@@ -45,6 +53,9 @@ module Capybara
|
|
45
53
|
|
46
54
|
##
|
47
55
|
# Checks if the page doesn't have the given path.
|
56
|
+
# By default, if passed a full url this will compare against the full url,
|
57
|
+
# if passed a path only the path+query portion will be compared, if passed a regexp
|
58
|
+
# the comparison will depend on the :url option
|
48
59
|
#
|
49
60
|
# @macro current_path_query_params
|
50
61
|
# @return [Boolean]
|
data/lib/capybara/session.rb
CHANGED
@@ -17,6 +17,14 @@ module Capybara
|
|
17
17
|
# session = Capybara::Session.new(:culerity)
|
18
18
|
# session.visit('http://www.google.com')
|
19
19
|
#
|
20
|
+
# When Capybara.threadsafe == true the sessions options will be initially set to the
|
21
|
+
# current values of the global options and a configuration block can be passed to the session initializer.
|
22
|
+
# For available options see {Capybara::SessionConfig::OPTIONS}
|
23
|
+
#
|
24
|
+
# session = Capybara::Session.new(:driver, MyRackApp) do |config|
|
25
|
+
# config.app_host = "http://my_host.dev"
|
26
|
+
# end
|
27
|
+
#
|
20
28
|
# Session provides a number of methods for controlling the navigation of the page, such as +visit+,
|
21
29
|
# +current_path, and so on. It also delegate a number of methods to a Capybara::Document, representing
|
22
30
|
# the current HTML document. This allows interaction:
|
@@ -50,7 +58,7 @@ module Capybara
|
|
50
58
|
]
|
51
59
|
SESSION_METHODS = [
|
52
60
|
:body, :html, :source, :current_url, :current_host, :current_path,
|
53
|
-
:execute_script, :evaluate_script, :visit, :go_back, :go_forward,
|
61
|
+
:execute_script, :evaluate_script, :visit, :refresh, :go_back, :go_forward,
|
54
62
|
:within, :within_element, :within_fieldset, :within_table, :within_frame, :switch_to_frame,
|
55
63
|
:current_window, :windows, :open_new_window, :switch_to_window, :within_window, :window_opened_by,
|
56
64
|
:save_page, :save_and_open_page, :save_screenshot,
|
@@ -69,10 +77,15 @@ module Capybara
|
|
69
77
|
|
70
78
|
def initialize(mode, app=nil)
|
71
79
|
raise TypeError, "The second parameter to Session::new should be a rack app if passed." if app && !app.respond_to?(:call)
|
80
|
+
@@instance_created = true
|
72
81
|
@mode = mode
|
73
82
|
@app = app
|
74
|
-
if
|
75
|
-
|
83
|
+
if block_given?
|
84
|
+
raise "A configuration block is only accepted when Capybara.threadsafe == true" unless Capybara.threadsafe
|
85
|
+
yield config if block_given?
|
86
|
+
end
|
87
|
+
if config.run_server and @app and driver.needs_server?
|
88
|
+
@server = Capybara::Server.new(@app, config.server_port, config.server_host, config.server_errors).boot
|
76
89
|
else
|
77
90
|
@server = nil
|
78
91
|
end
|
@@ -85,7 +98,9 @@ module Capybara
|
|
85
98
|
other_drivers = Capybara.drivers.keys.map { |key| key.inspect }
|
86
99
|
raise Capybara::DriverNotFoundError, "no driver called #{mode.inspect} was found, available drivers: #{other_drivers.join(', ')}"
|
87
100
|
end
|
88
|
-
Capybara.drivers[mode].call(app)
|
101
|
+
driver = Capybara.drivers[mode].call(app)
|
102
|
+
driver.session = self if driver.respond_to?(:session=)
|
103
|
+
driver
|
89
104
|
end
|
90
105
|
end
|
91
106
|
|
@@ -126,7 +141,7 @@ module Capybara
|
|
126
141
|
if @server and @server.error
|
127
142
|
# Force an explanation for the error being raised as the exception cause
|
128
143
|
begin
|
129
|
-
if
|
144
|
+
if config.raise_server_errors
|
130
145
|
raise CapybaraError, "Your application server raised an error - It has been raised in your test code because Capybara.raise_server_errors == true"
|
131
146
|
end
|
132
147
|
rescue CapybaraError
|
@@ -232,28 +247,42 @@ module Capybara
|
|
232
247
|
raise_server_error!
|
233
248
|
@touched = true
|
234
249
|
|
235
|
-
visit_uri = URI.parse(visit_uri.to_s)
|
250
|
+
visit_uri = ::Addressable::URI.parse(visit_uri.to_s)
|
236
251
|
|
237
252
|
uri_base = if @server
|
238
|
-
|
239
|
-
URI.parse(Capybara.app_host || "http://#{@server.host}:#{@server.port}")
|
253
|
+
::Addressable::URI.parse(config.app_host || "http://#{@server.host}:#{@server.port}")
|
240
254
|
else
|
241
|
-
|
255
|
+
config.app_host && ::Addressable::URI.parse(config.app_host)
|
242
256
|
end
|
243
257
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
# deploying to a subdirectory and/or single page apps where only the url fragment changes
|
248
|
-
if visit_uri.scheme.nil? && uri_base
|
249
|
-
visit_uri.path = uri_base.path + visit_uri.path
|
250
|
-
end
|
258
|
+
if uri_base && [nil, 'http', 'https'].include?(visit_uri.scheme)
|
259
|
+
if visit_uri.relative?
|
260
|
+
uri_base.port ||= @server.port if @server && config.always_include_port
|
251
261
|
|
252
|
-
|
262
|
+
visit_uri_parts = visit_uri.to_hash.delete_if { |k,v| v.nil? }
|
263
|
+
|
264
|
+
# Useful to people deploying to a subdirectory
|
265
|
+
# and/or single page apps where only the url fragment changes
|
266
|
+
visit_uri_parts[:path] = uri_base.path + visit_uri.path
|
267
|
+
|
268
|
+
visit_uri = uri_base.merge(visit_uri_parts)
|
269
|
+
else
|
270
|
+
visit_uri.port ||= @server.port if @server && config.always_include_port
|
271
|
+
end
|
272
|
+
end
|
253
273
|
|
254
274
|
driver.visit(visit_uri.to_s)
|
255
275
|
end
|
256
276
|
|
277
|
+
##
|
278
|
+
#
|
279
|
+
# Refresh the page
|
280
|
+
#
|
281
|
+
def refresh
|
282
|
+
raise_server_error!
|
283
|
+
driver.refresh
|
284
|
+
end
|
285
|
+
|
257
286
|
##
|
258
287
|
#
|
259
288
|
# Move back a single entry in the browser's history.
|
@@ -457,14 +486,14 @@ module Capybara
|
|
457
486
|
# @raise [Capybara::WindowError] if no window matches given block
|
458
487
|
# @overload switch_to_window(window)
|
459
488
|
# @param window [Capybara::Window] window that should be switched to
|
460
|
-
# @raise [Capybara::Driver::Base#no_such_window_error] if
|
489
|
+
# @raise [Capybara::Driver::Base#no_such_window_error] if nonexistent (e.g. closed) window was passed
|
461
490
|
#
|
462
491
|
# @return [Capybara::Window] window that has been switched to
|
463
|
-
# @raise [Capybara::ScopeError] if this method is invoked inside `within
|
464
|
-
# `within_frame`
|
492
|
+
# @raise [Capybara::ScopeError] if this method is invoked inside `within` or
|
493
|
+
# `within_frame` methods
|
465
494
|
# @raise [ArgumentError] if both or neither arguments were provided
|
466
495
|
#
|
467
|
-
def switch_to_window(window = nil, options= {})
|
496
|
+
def switch_to_window(window = nil, options= {}, &window_locator)
|
468
497
|
options, window = window, nil if window.is_a? Hash
|
469
498
|
|
470
499
|
block_given = block_given?
|
@@ -472,34 +501,12 @@ module Capybara
|
|
472
501
|
raise ArgumentError, "`switch_to_window` can take either a block or a window, not both"
|
473
502
|
elsif !window && !block_given
|
474
503
|
raise ArgumentError, "`switch_to_window`: either window or block should be provided"
|
475
|
-
elsif scopes.
|
504
|
+
elsif !scopes.last.nil?
|
476
505
|
raise Capybara::ScopeError, "`switch_to_window` is not supposed to be invoked from "\
|
477
|
-
"`within`
|
506
|
+
"`within` or `within_frame` blocks."
|
478
507
|
end
|
479
508
|
|
480
|
-
|
481
|
-
driver.switch_to_window(window.handle)
|
482
|
-
window
|
483
|
-
else
|
484
|
-
wait_time = Capybara::Queries::BaseQuery.wait(options)
|
485
|
-
document.synchronize(wait_time, errors: [Capybara::WindowError]) do
|
486
|
-
original_window_handle = driver.current_window_handle
|
487
|
-
begin
|
488
|
-
driver.window_handles.each do |handle|
|
489
|
-
driver.switch_to_window handle
|
490
|
-
if yield
|
491
|
-
return Window.new(self, handle)
|
492
|
-
end
|
493
|
-
end
|
494
|
-
rescue => e
|
495
|
-
driver.switch_to_window(original_window_handle)
|
496
|
-
raise e
|
497
|
-
else
|
498
|
-
driver.switch_to_window(original_window_handle)
|
499
|
-
raise Capybara::WindowError, "Could not find a window matching block/lambda"
|
500
|
-
end
|
501
|
-
end
|
502
|
-
end
|
509
|
+
_switch_to_window(window, options, &window_locator)
|
503
510
|
end
|
504
511
|
|
505
512
|
##
|
@@ -512,7 +519,7 @@ module Capybara
|
|
512
519
|
# @overload within_window(window) { do_something }
|
513
520
|
# @param window [Capybara::Window] instance of `Capybara::Window` class
|
514
521
|
# that will be switched to
|
515
|
-
# @raise [driver#no_such_window_error] if
|
522
|
+
# @raise [driver#no_such_window_error] if nonexistent (e.g. closed) window was passed
|
516
523
|
# @overload within_window(proc_or_lambda) { do_something }
|
517
524
|
# @param lambda [Proc] lambda. First window for which lambda
|
518
525
|
# returns a value other than false or nil will be switched to.
|
@@ -523,30 +530,35 @@ module Capybara
|
|
523
530
|
# @deprecated Pass window or lambda instead
|
524
531
|
# @param [String] handle, name, url or title of the window
|
525
532
|
#
|
526
|
-
# @raise [Capybara::ScopeError] if this method is invoked inside `
|
527
|
-
# `within_frame` or `within_window` methods
|
533
|
+
# @raise [Capybara::ScopeError] if this method is invoked inside `within_frame` method
|
528
534
|
# @return value returned by the block
|
529
535
|
#
|
530
536
|
def within_window(window_or_handle)
|
531
537
|
if window_or_handle.instance_of?(Capybara::Window)
|
532
538
|
original = current_window
|
533
|
-
switch_to_window(window_or_handle) unless original == window_or_handle
|
534
539
|
scopes << nil
|
535
540
|
begin
|
536
|
-
|
541
|
+
_switch_to_window(window_or_handle) unless original == window_or_handle
|
542
|
+
begin
|
543
|
+
yield
|
544
|
+
ensure
|
545
|
+
_switch_to_window(original) unless original == window_or_handle
|
546
|
+
end
|
537
547
|
ensure
|
538
|
-
|
539
|
-
switch_to_window(original) unless original == window_or_handle
|
548
|
+
scopes.pop
|
540
549
|
end
|
541
550
|
elsif window_or_handle.is_a?(Proc)
|
542
551
|
original = current_window
|
543
|
-
switch_to_window { window_or_handle.call }
|
544
552
|
scopes << nil
|
545
553
|
begin
|
546
|
-
|
554
|
+
_switch_to_window { window_or_handle.call }
|
555
|
+
begin
|
556
|
+
yield
|
557
|
+
ensure
|
558
|
+
_switch_to_window(original)
|
559
|
+
end
|
547
560
|
ensure
|
548
|
-
|
549
|
-
switch_to_window(original)
|
561
|
+
scopes.pop
|
550
562
|
end
|
551
563
|
else
|
552
564
|
offending_line = caller.first
|
@@ -557,7 +569,7 @@ module Capybara
|
|
557
569
|
scopes << nil
|
558
570
|
driver.within_window(window_or_handle) { yield }
|
559
571
|
ensure
|
560
|
-
|
572
|
+
scopes.pop
|
561
573
|
end
|
562
574
|
end
|
563
575
|
end
|
@@ -578,7 +590,7 @@ module Capybara
|
|
578
590
|
old_handles = driver.window_handles
|
579
591
|
block.call
|
580
592
|
|
581
|
-
wait_time = Capybara::Queries::BaseQuery.wait(options)
|
593
|
+
wait_time = Capybara::Queries::BaseQuery.wait(options, config.default_max_wait_time)
|
582
594
|
document.synchronize(wait_time, errors: [Capybara::WindowError]) do
|
583
595
|
opened_handles = (driver.window_handles - old_handles)
|
584
596
|
if opened_handles.size != 1
|
@@ -628,18 +640,45 @@ module Capybara
|
|
628
640
|
element_script_result(result)
|
629
641
|
end
|
630
642
|
|
643
|
+
##
|
644
|
+
#
|
645
|
+
# Evaluate the given JavaScript and obtain the result from a callback function which will be passed as the last argument to the script.
|
646
|
+
#
|
647
|
+
# @param [String] script A string of JavaScript to evaluate
|
648
|
+
# @return [Object] The result of the evaluated JavaScript (may be driver specific)
|
649
|
+
#
|
650
|
+
def evaluate_async_script(script, *args)
|
651
|
+
@touched = true
|
652
|
+
result = if args.empty?
|
653
|
+
driver.evaluate_async_script(script)
|
654
|
+
else
|
655
|
+
raise Capybara::NotSupportedByDriverError, "The current driver does not support evaluate_async_script arguments" if driver.method(:evaluate_async_script).arity == 1
|
656
|
+
driver.evaluate_async_script(script, *args.map { |arg| arg.is_a?(Capybara::Node::Element) ? arg.base : arg} )
|
657
|
+
end
|
658
|
+
element_script_result(result)
|
659
|
+
end
|
660
|
+
|
631
661
|
##
|
632
662
|
#
|
633
663
|
# Execute the block, accepting a alert.
|
634
664
|
#
|
635
665
|
# @!macro modal_params
|
666
|
+
# Expects a block whose actions will trigger the display modal to appear
|
667
|
+
# @example
|
668
|
+
# $0 do
|
669
|
+
# click_link('link that triggers appearance of system modal')
|
670
|
+
# end
|
636
671
|
# @overload $0(text, options = {}, &blk)
|
637
672
|
# @param text [String, Regexp] Text or regex to match against the text in the modal. If not provided any modal is matched
|
673
|
+
# @option options [Numeric] :wait (Capybara.default_max_wait_time) Maximum time to wait for the modal to appear after executing the block.
|
674
|
+
# @yield Block whose actions will trigger the system modal
|
638
675
|
# @overload $0(options = {}, &blk)
|
639
|
-
#
|
676
|
+
# @option options [Numeric] :wait (Capybara.default_max_wait_time) Maximum time to wait for the modal to appear after executing the block.
|
677
|
+
# @yield Block whose actions will trigger the system modal
|
640
678
|
# @return [String] the message shown in the modal
|
641
679
|
# @raise [Capybara::ModalNotFound] if modal dialog hasn't been found
|
642
680
|
#
|
681
|
+
#
|
643
682
|
def accept_alert(text_or_options=nil, options={}, &blk)
|
644
683
|
accept_modal(:alert, text_or_options, options, &blk)
|
645
684
|
end
|
@@ -701,7 +740,7 @@ module Capybara
|
|
701
740
|
#
|
702
741
|
def save_page(path = nil)
|
703
742
|
path = prepare_path(path, 'html')
|
704
|
-
File.write(path, Capybara::Helpers.inject_asset_host(body), mode: 'wb')
|
743
|
+
File.write(path, Capybara::Helpers.inject_asset_host(body, config.asset_host), mode: 'wb')
|
705
744
|
path
|
706
745
|
end
|
707
746
|
|
@@ -786,7 +825,49 @@ module Capybara
|
|
786
825
|
scope
|
787
826
|
end
|
788
827
|
|
828
|
+
##
|
829
|
+
#
|
830
|
+
# Yield a block using a specific wait time
|
831
|
+
#
|
832
|
+
def using_wait_time(seconds)
|
833
|
+
if Capybara.threadsafe
|
834
|
+
begin
|
835
|
+
previous_wait_time = config.default_max_wait_time
|
836
|
+
config.default_max_wait_time = seconds
|
837
|
+
yield
|
838
|
+
ensure
|
839
|
+
config.default_max_wait_time = previous_wait_time
|
840
|
+
end
|
841
|
+
else
|
842
|
+
Capybara.using_wait_time(seconds) { yield }
|
843
|
+
end
|
844
|
+
end
|
845
|
+
|
846
|
+
##
|
847
|
+
#
|
848
|
+
# Accepts a block to set the configuration options if Capybara.threadsafe == true. Note that some options only have an effect
|
849
|
+
# if set at initialization time, so look at the configuration block that can be passed to the initializer too
|
850
|
+
#
|
851
|
+
def configure
|
852
|
+
raise "Session configuration is only supported when Capybara.threadsafe == true" unless Capybara.threadsafe
|
853
|
+
yield config
|
854
|
+
end
|
855
|
+
|
856
|
+
def self.instance_created?
|
857
|
+
@@instance_created
|
858
|
+
end
|
859
|
+
|
860
|
+
def config
|
861
|
+
@config ||= if Capybara.threadsafe
|
862
|
+
Capybara.session_options.dup
|
863
|
+
else
|
864
|
+
Capybara::ReadOnlySessionConfig.new(Capybara.session_options)
|
865
|
+
end
|
866
|
+
end
|
789
867
|
private
|
868
|
+
|
869
|
+
@@instance_created = false
|
870
|
+
|
790
871
|
def accept_modal(type, text_or_options, options, &blk)
|
791
872
|
driver.accept_modal(type, modal_options(text_or_options, options), &blk)
|
792
873
|
end
|
@@ -798,7 +879,7 @@ module Capybara
|
|
798
879
|
def modal_options(text_or_options, options)
|
799
880
|
text_or_options, options = nil, text_or_options if text_or_options.is_a?(Hash)
|
800
881
|
options[:text] ||= text_or_options unless text_or_options.nil?
|
801
|
-
options[:wait] ||=
|
882
|
+
options[:wait] ||= config.default_max_wait_time
|
802
883
|
options
|
803
884
|
end
|
804
885
|
|
@@ -814,10 +895,10 @@ module Capybara
|
|
814
895
|
end
|
815
896
|
|
816
897
|
def prepare_path(path, extension)
|
817
|
-
if
|
818
|
-
path = File.expand_path(path || default_fn(extension),
|
898
|
+
if config.save_path || config.save_and_open_page_path.nil?
|
899
|
+
path = File.expand_path(path || default_fn(extension), config.save_path)
|
819
900
|
else
|
820
|
-
path = File.expand_path(default_fn(extension),
|
901
|
+
path = File.expand_path(default_fn(extension), config.save_and_open_page_path) if path.nil?
|
821
902
|
end
|
822
903
|
FileUtils.mkdir_p(File.dirname(path))
|
823
904
|
path
|
@@ -862,5 +943,37 @@ module Capybara
|
|
862
943
|
end
|
863
944
|
end
|
864
945
|
end
|
946
|
+
|
947
|
+
def _switch_to_window(window = nil, options= {})
|
948
|
+
options, window = window, nil if window.is_a? Hash
|
949
|
+
|
950
|
+
raise Capybara::ScopeError, "Window cannot be switched inside a `within_frame` block" if scopes.include?(:frame)
|
951
|
+
raise Capybara::ScopeError, "Window cannot be switch inside a `within` block" unless scopes.last.nil?
|
952
|
+
|
953
|
+
if window
|
954
|
+
driver.switch_to_window(window.handle)
|
955
|
+
window
|
956
|
+
else
|
957
|
+
wait_time = Capybara::Queries::BaseQuery.wait(options, config.default_max_wait_time)
|
958
|
+
document.synchronize(wait_time, errors: [Capybara::WindowError]) do
|
959
|
+
original_window_handle = driver.current_window_handle
|
960
|
+
begin
|
961
|
+
driver.window_handles.each do |handle|
|
962
|
+
driver.switch_to_window handle
|
963
|
+
if yield
|
964
|
+
return Window.new(self, handle)
|
965
|
+
end
|
966
|
+
end
|
967
|
+
rescue => e
|
968
|
+
driver.switch_to_window(original_window_handle)
|
969
|
+
raise e
|
970
|
+
else
|
971
|
+
driver.switch_to_window(original_window_handle)
|
972
|
+
raise Capybara::WindowError, "Could not find a window matching block/lambda"
|
973
|
+
end
|
974
|
+
end
|
975
|
+
end
|
976
|
+
end
|
977
|
+
|
865
978
|
end
|
866
979
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
ThisFileHAsNoExtension
|
@@ -1,8 +1,8 @@
|
|
1
1
|
var activeRequests = 0;
|
2
2
|
$(function() {
|
3
3
|
$('#change').text('I changed it');
|
4
|
-
$('#drag').draggable();
|
5
|
-
$('#drop').droppable({
|
4
|
+
$('#drag, #drag_scroll').draggable();
|
5
|
+
$('#drop, #drop_scroll').droppable({
|
6
6
|
drop: function(event, ui) {
|
7
7
|
ui.draggable.remove();
|
8
8
|
$(this).html('Dropped!');
|
@@ -37,6 +37,9 @@ $(function() {
|
|
37
37
|
$('#with_change_event').change(function() {
|
38
38
|
$('body').append($('<p class="change_event_triggered"></p>').text(this.value));
|
39
39
|
});
|
40
|
+
$('#with_change_event').on('input', function() {
|
41
|
+
$('body').append($('<p class="input_event_triggered"></p>').text(this.value));
|
42
|
+
});
|
40
43
|
$('#checkbox_with_event').click(function() {
|
41
44
|
$('body').append('<p id="checkbox_event_triggered">Checkbox event triggered</p>');
|
42
45
|
});
|
@@ -58,7 +61,7 @@ $(function() {
|
|
58
61
|
$('#change-title').click(function() {
|
59
62
|
setTimeout(function() {
|
60
63
|
$('title').text('changed title')
|
61
|
-
},
|
64
|
+
}, 400)
|
62
65
|
});
|
63
66
|
$('#click-test').on({
|
64
67
|
dblclick: function() {
|
@@ -87,6 +90,10 @@ $(function() {
|
|
87
90
|
$(link).attr('opened', 'true');
|
88
91
|
}, 3000);
|
89
92
|
});
|
93
|
+
$('#alert-page-change').click(function() {
|
94
|
+
alert('Page is changing');
|
95
|
+
return true;
|
96
|
+
});
|
90
97
|
$('#open-confirm').click(function() {
|
91
98
|
if(confirm('Confirm opened')) {
|
92
99
|
$(this).attr('confirmed', 'true');
|
@@ -102,6 +109,14 @@ $(function() {
|
|
102
109
|
$(this).attr('response', response);
|
103
110
|
}
|
104
111
|
});
|
112
|
+
$('#open-prompt-with-default').click(function() {
|
113
|
+
var response = prompt('Prompt opened', 'Default value!');
|
114
|
+
if(response === null) {
|
115
|
+
$(this).attr('response', 'dismissed');
|
116
|
+
} else {
|
117
|
+
$(this).attr('response', response);
|
118
|
+
}
|
119
|
+
});
|
105
120
|
$('#open-twice').click(function() {
|
106
121
|
if (confirm('Are you sure?')) {
|
107
122
|
if (!confirm('Are you really sure?')) {
|
@@ -47,6 +47,14 @@ Capybara::SpecHelper.spec '#accept_alert', requires: [:modals] do
|
|
47
47
|
expect(message).to eq('Alert opened [*Yay?*]')
|
48
48
|
end
|
49
49
|
|
50
|
+
it "should handle the alert if the page changes" do
|
51
|
+
@session.accept_alert do
|
52
|
+
@session.click_link('Alert page change')
|
53
|
+
sleep 1 # ensure page change occurs before the accept_alert block exits
|
54
|
+
end
|
55
|
+
expect(@session).to have_current_path('/with_html')
|
56
|
+
end
|
57
|
+
|
50
58
|
context "with an asynchronous alert" do
|
51
59
|
it "should accept the alert" do
|
52
60
|
@session.accept_alert do
|
@@ -63,7 +71,7 @@ Capybara::SpecHelper.spec '#accept_alert', requires: [:modals] do
|
|
63
71
|
end
|
64
72
|
|
65
73
|
it "should allow to adjust the delay" do
|
66
|
-
@session.accept_alert wait:
|
74
|
+
@session.accept_alert wait: 10 do
|
67
75
|
@session.click_link('Open slow alert')
|
68
76
|
end
|
69
77
|
expect(@session).to have_xpath("//a[@id='open-slow-alert' and @opened='true']")
|