capybara 2.15.4 → 2.16.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/History.md +17 -1
- data/README.md +12 -8
- data/lib/capybara/config.rb +2 -1
- data/lib/capybara/driver/base.rb +4 -0
- data/lib/capybara/minitest/spec.rb +3 -3
- data/lib/capybara/node/actions.rb +8 -8
- data/lib/capybara/node/finders.rb +1 -1
- data/lib/capybara/queries/current_path_query.rb +11 -8
- data/lib/capybara/queries/selector_query.rb +2 -3
- data/lib/capybara/queries/text_query.rb +1 -1
- data/lib/capybara/selenium/driver.rb +28 -13
- data/lib/capybara/selenium/node.rb +9 -0
- data/lib/capybara/session.rb +18 -0
- data/lib/capybara/session/matchers.rb +15 -7
- data/lib/capybara/spec/public/test.js +8 -0
- data/lib/capybara/spec/session/accept_prompt_spec.rb +22 -0
- 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/has_current_path_spec.rb +41 -4
- data/lib/capybara/spec/views/with_fixed_header_footer.erb +17 -0
- data/lib/capybara/spec/views/with_js.erb +4 -0
- data/lib/capybara/version.rb +1 -1
- data/spec/selenium_spec_marionette.rb +2 -2
- data/spec/shared_selenium_session.rb +24 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ee26f641aae1c298d4f5c3d7e8a98376c133c1e2d6ac90b3a3a274ae0dddd13f
|
4
|
+
data.tar.gz: 12a92103cb8b03439ba345d2433385d5d8ec698713c1fda8d8d496c1d7a5db4a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b63bffec8f36f684af468659992edc8368844838a7f92f0cacaca2cfb070c8d5d62baebc6308dc60bac7646a0e4a7221eca416014cbdc2a0f82ce189dedc1172
|
7
|
+
data.tar.gz: 26d2d8319225c85ef3ea5c1c546f321a92063adcf08a97ed1e530782e7af18aa9c7b2e8c59faaac2bf02c5a3d0d7d37d82a124dc1a0472605296745fe26f2ca8
|
data/History.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
# Version 2.16.0
|
2
|
+
Release date: 2017-11-13
|
3
|
+
|
4
|
+
### Added
|
5
|
+
|
6
|
+
* Attempt to move element into view when selenium doesn't correctly do it - See PR #1917 [Thomas Walpole]
|
7
|
+
* `current_path` matchers will now autodetect path vs url based on string to be matched. Deprecates
|
8
|
+
`:only_path` in favor of `:ignore_query` option [Thomas Walpole]
|
9
|
+
* Session#evaluate_async_script [Thomas Walpole]
|
10
|
+
|
11
|
+
### Fixed
|
12
|
+
|
13
|
+
* Default prompt value when using headless Chrome works correctly [Thomas Walpole]
|
14
|
+
* Support new modal error returned by selenium-webdriver 3.7 for W3C drivers [Thomas Walpole]
|
15
|
+
* Calling `respond_to?` on the object passed to `Capybara.configure` block - Issue #1935
|
16
|
+
|
1
17
|
# Version 2.15.4
|
2
18
|
Release date: 2017-10-07
|
3
19
|
|
@@ -235,7 +251,7 @@ Release date: 2016-09-19
|
|
235
251
|
|
236
252
|
Release date: 2016-08-25
|
237
253
|
|
238
|
-
###Fixed
|
254
|
+
### Fixed
|
239
255
|
|
240
256
|
* Fixed error message from have_text when text is not found but contains regex special characters [Ryunosuke Sato]
|
241
257
|
* Warn when :exact option is passed that has no effect [Thomas Walpole]
|
data/README.md
CHANGED
@@ -549,7 +549,7 @@ If you find yourself needing to use this a lot you may be better off adding a [c
|
|
549
549
|
```ruby
|
550
550
|
find_field('First Name'){ |el| el['data-xyz'] == '123' }
|
551
551
|
find("#img_loading"){ |img| img['complete'] == true }
|
552
|
-
|
552
|
+
```
|
553
553
|
|
554
554
|
**Note**: `find` will wait for an element to appear on the page, as explained in the
|
555
555
|
Ajax section. If the element does not appear it will raise an error.
|
@@ -894,7 +894,7 @@ To permanently switch the current session to a different session
|
|
894
894
|
|
895
895
|
```ruby
|
896
896
|
Capybara.session_name = "some other session"
|
897
|
-
|
897
|
+
```
|
898
898
|
|
899
899
|
### <a name="using-sessions-manually"></a>Using sessions manually
|
900
900
|
|
@@ -1053,7 +1053,9 @@ In normal mode most of Capybara's configuration options are global settings whic
|
|
1053
1053
|
if using multiple sessions and wanting to change a setting for only one of the sessions. To provide
|
1054
1054
|
support for this type of usage Capybara now provides a "threadsafe" mode which can be enabled by setting
|
1055
1055
|
|
1056
|
-
|
1056
|
+
```ruby
|
1057
|
+
Capybara.threadsafe = true
|
1058
|
+
```
|
1057
1059
|
|
1058
1060
|
This setting can only be changed before any sessions have been created. In "threadsafe" mode the following
|
1059
1061
|
behaviors of Capybara change
|
@@ -1063,11 +1065,13 @@ behaviors of Capybara change
|
|
1063
1065
|
`app`, `reuse_server`, `default_driver`, `javascript_driver`, and (obviously) `threadsafe`. Any drivers and servers
|
1064
1066
|
registered through `register_driver` and `register_server` are also global.
|
1065
1067
|
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1068
|
+
```ruby
|
1069
|
+
my_session = Capybara::Session.new(:driver, some_app) do |config|
|
1070
|
+
config.automatic_label_click = true # only set for my_session
|
1071
|
+
end
|
1072
|
+
my_session.config.default_max_wait_time = 10 # only set for my_session
|
1073
|
+
Capybara.default_max_wait_time = 2 # will not change the default_max_wait in my_session
|
1074
|
+
```
|
1071
1075
|
|
1072
1076
|
* `current_driver` and `session_name` are thread specific. This means that `using_session` and
|
1073
1077
|
`using_driver` also only affect the current thread.
|
data/lib/capybara/config.rb
CHANGED
@@ -108,6 +108,7 @@ module Capybara
|
|
108
108
|
end
|
109
109
|
end
|
110
110
|
|
111
|
+
# @api private
|
111
112
|
class ConfigureDeprecator
|
112
113
|
def initialize(config)
|
113
114
|
@config = config
|
@@ -125,7 +126,7 @@ module Capybara
|
|
125
126
|
end
|
126
127
|
|
127
128
|
def respond_to_missing?(m, include_private = false)
|
128
|
-
@config.
|
129
|
+
@config.respond_to?(m) || Capybara.respond_to?(m) || super
|
129
130
|
end
|
130
131
|
end
|
131
132
|
end
|
data/lib/capybara/driver/base.rb
CHANGED
@@ -42,6 +42,10 @@ class Capybara::Driver::Base
|
|
42
42
|
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#evaluate_script'
|
43
43
|
end
|
44
44
|
|
45
|
+
def evaluate_async_script(script, *args)
|
46
|
+
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#evaluate_script_asnyc'
|
47
|
+
end
|
48
|
+
|
45
49
|
def save_screenshot(path, options={})
|
46
50
|
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#save_screenshot'
|
47
51
|
end
|
@@ -8,13 +8,13 @@ module Capybara
|
|
8
8
|
infect_an_assertion "refute_#{assertion}", "wont_have_#{assertion}", :reverse
|
9
9
|
end
|
10
10
|
|
11
|
-
(%w(selector xpath css link button field select table checked_field unchecked_field).
|
11
|
+
(%w(selector xpath css link button field select table checked_field unchecked_field).flat_map do |assertion|
|
12
12
|
[["assert_#{assertion}", "must_have_#{assertion}"],
|
13
13
|
["refute_#{assertion}", "wont_have_#{assertion}"]]
|
14
|
-
end
|
14
|
+
end + %w(selector xpath css).flat_map do |assertion|
|
15
15
|
[["assert_matches_#{assertion}", "must_match_#{assertion}"],
|
16
16
|
["refute_matches_#{assertion}", "wont_match_#{assertion}"]]
|
17
|
-
end
|
17
|
+
end).each do |(meth, new_name)|
|
18
18
|
self.class_eval <<-EOM, __FILE__, __LINE__ + 1
|
19
19
|
def #{new_name} *args, &block
|
20
20
|
::Minitest::Expectation.new(self, ::Minitest::Spec.current).#{new_name}(*args, &block)
|
@@ -5,8 +5,8 @@ module Capybara
|
|
5
5
|
|
6
6
|
##
|
7
7
|
#
|
8
|
-
# Finds a button or link
|
9
|
-
#
|
8
|
+
# Finds a button or link and clicks it. See {Capybara::Node::Actions#click_button} and
|
9
|
+
# {Capybara::Node::Actions#click_link} for what locator will match against for each type of element
|
10
10
|
# @!macro waiting_behavior
|
11
11
|
# If the driver is capable of executing JavaScript, +$0+ will wait for a set amount of time
|
12
12
|
# and continuously retry finding the element until either the element is found or the time
|
@@ -16,7 +16,7 @@ module Capybara
|
|
16
16
|
#
|
17
17
|
# @overload click_link_or_button([locator], options)
|
18
18
|
#
|
19
|
-
# @param [String] locator
|
19
|
+
# @param [String] locator See {Capybara::Node::Actions#click_button} and {Capybara::Node::Actions#click_link}
|
20
20
|
#
|
21
21
|
# @return [Capybara::Node::Element] The element clicked
|
22
22
|
#
|
@@ -80,7 +80,7 @@ module Capybara
|
|
80
80
|
# @option options [String] :id Match fields that match the id attribute
|
81
81
|
# @option options [String] :name Match fields that match the name attribute
|
82
82
|
# @option options [String] :placeholder Match fields that match the placeholder attribute
|
83
|
-
# @option options [String, Array<String>] :class Match
|
83
|
+
# @option options [String, Array<String>] :class Match fields that match the class(es) provided
|
84
84
|
#
|
85
85
|
# @return [Capybara::Node::Element] The element filled_in
|
86
86
|
def fill_in(locator, options={})
|
@@ -108,7 +108,7 @@ module Capybara
|
|
108
108
|
# @option options [String] :option Value of the radio_button to choose
|
109
109
|
# @option options [String] :id Match fields that match the id attribute
|
110
110
|
# @option options [String] :name Match fields that match the name attribute
|
111
|
-
# @option options [String, Array<String>] :class Match
|
111
|
+
# @option options [String, Array<String>] :class Match fields that match the class(es) provided
|
112
112
|
# @macro waiting_behavior
|
113
113
|
# @macro label_click
|
114
114
|
#
|
@@ -131,7 +131,7 @@ module Capybara
|
|
131
131
|
# @option options [String] :option Value of the checkbox to select
|
132
132
|
# @option options [String] id Match fields that match the id attribute
|
133
133
|
# @option options [String] name Match fields that match the name attribute
|
134
|
-
# @option options [String, Array<String>] :class Match
|
134
|
+
# @option options [String, Array<String>] :class Match fields that match the class(es) provided
|
135
135
|
# @macro label_click
|
136
136
|
# @macro waiting_behavior
|
137
137
|
#
|
@@ -154,7 +154,7 @@ module Capybara
|
|
154
154
|
# @option options [String] :option Value of the checkbox to deselect
|
155
155
|
# @option options [String] id Match fields that match the id attribute
|
156
156
|
# @option options [String] name Match fields that match the name attribute
|
157
|
-
# @option options [String, Array<String>] :class Match
|
157
|
+
# @option options [String, Array<String>] :class Match fields that match the class(es) provided
|
158
158
|
# @macro label_click
|
159
159
|
# @macro waiting_behavior
|
160
160
|
#
|
@@ -229,7 +229,7 @@ module Capybara
|
|
229
229
|
# @option options [Boolean] multiple Match field which allows multiple file selection
|
230
230
|
# @option options [String] id Match fields that match the id attribute
|
231
231
|
# @option options [String] name Match fields that match the name attribute
|
232
|
-
# @option options [String, Array<String>] :class Match
|
232
|
+
# @option options [String, Array<String>] :class Match fields that match the class(es) provided
|
233
233
|
# @option options [true, Hash] make_visible A Hash of CSS styles to change before attempting to attach the file, if `true` { opacity: 1, display: 'block', visibility: 'visible' } is used (may not be supported by all drivers)
|
234
234
|
#
|
235
235
|
# @return [Capybara::Node::Element] The file field element
|
@@ -171,7 +171,7 @@ module Capybara
|
|
171
171
|
# @option options [String] id Match buttons with the id provided
|
172
172
|
# @option options [String] title Match buttons with the title provided
|
173
173
|
# @option options [String] value Match buttons with the value provided
|
174
|
-
# @option options [String, Array<String>] class Match
|
174
|
+
# @option options [String, Array<String>] class Match buttons that match the class(es) provided
|
175
175
|
# @return [Capybara::Node::Element] The found element
|
176
176
|
#
|
177
177
|
def find_button(locator=nil, options={}, &optional_filter_block)
|
@@ -8,22 +8,25 @@ module Capybara
|
|
8
8
|
def initialize(expected_path, options = {})
|
9
9
|
super(options)
|
10
10
|
@expected_path = expected_path
|
11
|
+
warn "DEPRECATED: The :only_path option is deprecated in favor of the :ignore_query option" if options.has_key?(:only_path)
|
12
|
+
|
11
13
|
@options = {
|
12
|
-
url:
|
13
|
-
only_path: false
|
14
|
+
url: !@expected_path.is_a?(Regexp) && !::Addressable::URI.parse(@expected_path || "").hostname.nil?,
|
15
|
+
only_path: false,
|
16
|
+
ignore_query: false }.merge(options)
|
14
17
|
assert_valid_keys
|
15
18
|
end
|
16
19
|
|
17
20
|
def resolves_for?(session)
|
21
|
+
uri = ::Addressable::URI.parse(session.current_url)
|
22
|
+
uri.query = nil if uri && options[:ignore_query]
|
18
23
|
@actual_path = if options[:url]
|
19
|
-
|
24
|
+
uri.to_s
|
20
25
|
else
|
21
|
-
uri = ::Addressable::URI.parse(session.current_url)
|
22
|
-
|
23
26
|
if options[:only_path]
|
24
|
-
uri
|
27
|
+
uri && uri.path
|
25
28
|
else
|
26
|
-
uri
|
29
|
+
uri && uri.request_uri
|
27
30
|
end
|
28
31
|
end
|
29
32
|
|
@@ -50,7 +53,7 @@ module Capybara
|
|
50
53
|
end
|
51
54
|
|
52
55
|
def valid_keys
|
53
|
-
[:wait, :url, :only_path]
|
56
|
+
[:wait, :url, :only_path, :ignore_query]
|
54
57
|
end
|
55
58
|
|
56
59
|
def assert_valid_keys
|
@@ -17,7 +17,6 @@ module Capybara
|
|
17
17
|
if args[0].is_a?(Symbol)
|
18
18
|
@selector = Selector.all.fetch(args.shift) do |selector_type|
|
19
19
|
raise ArgumentError, "Unknown selector type (:#{selector_type})"
|
20
|
-
nil
|
21
20
|
end
|
22
21
|
@locator = args.shift
|
23
22
|
else
|
@@ -26,7 +25,7 @@ module Capybara
|
|
26
25
|
end
|
27
26
|
@selector ||= Selector.all[session_options.default_selector]
|
28
27
|
|
29
|
-
warn "Unused parameters passed to #{self.class.name} : #{args
|
28
|
+
warn "Unused parameters passed to #{self.class.name} : #{args}" unless args.empty?
|
30
29
|
|
31
30
|
# for compatibility with Capybara 2.0
|
32
31
|
if session_options.exact_options and @selector == Selector.all[:option]
|
@@ -235,7 +234,7 @@ module Capybara
|
|
235
234
|
|
236
235
|
def warn_exact_usage
|
237
236
|
if options.has_key?(:exact) && !supports_exact?
|
238
|
-
warn "The :exact option only has an effect on queries using the XPath#is method. Using it with the query \"#{expression
|
237
|
+
warn "The :exact option only has an effect on queries using the XPath#is method. Using it with the query \"#{expression}\" has no effect."
|
239
238
|
end
|
240
239
|
end
|
241
240
|
|
@@ -16,7 +16,7 @@ module Capybara
|
|
16
16
|
@expected_text = Capybara::Helpers.normalize_whitespace(@expected_text)
|
17
17
|
end
|
18
18
|
@search_regexp = Capybara::Helpers.to_regexp(@expected_text, nil, exact?)
|
19
|
-
warn "Unused parameters passed to #{self.class.name} : #{args
|
19
|
+
warn "Unused parameters passed to #{self.class.name} : #{args}" unless args.empty?
|
20
20
|
assert_valid_keys
|
21
21
|
end
|
22
22
|
|
@@ -24,7 +24,6 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
24
24
|
|
25
25
|
@w3c = ((defined?(Selenium::WebDriver::Remote::W3CCapabilities) && @browser.capabilities.is_a?(Selenium::WebDriver::Remote::W3CCapabilities)) ||
|
26
26
|
(defined?(Selenium::WebDriver::Remote::W3C::Capabilities) && @browser.capabilities.is_a?(Selenium::WebDriver::Remote::W3C::Capabilities)))
|
27
|
-
|
28
27
|
main = Process.pid
|
29
28
|
at_exit do
|
30
29
|
# Store the exit status of the test run since it goes away after calling the at_exit proc...
|
@@ -114,6 +113,12 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
114
113
|
unwrap_script_result(result)
|
115
114
|
end
|
116
115
|
|
116
|
+
def evaluate_async_script(script, *args)
|
117
|
+
browser.manage.timeouts.script_timeout = Capybara.default_max_wait_time
|
118
|
+
result = browser.execute_async_script(script, *args.map { |arg| arg.is_a?(Capybara::Selenium::Node) ? arg.native : arg} )
|
119
|
+
unwrap_script_result(result)
|
120
|
+
end
|
121
|
+
|
117
122
|
def save_screenshot(path, _options={})
|
118
123
|
browser.save_screenshot(path)
|
119
124
|
end
|
@@ -164,7 +169,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
164
169
|
begin
|
165
170
|
@browser.switch_to.alert.accept
|
166
171
|
sleep 0.25 # allow time for the modal to be handled
|
167
|
-
rescue
|
172
|
+
rescue modal_error
|
168
173
|
# The alert is now gone - nothing to do
|
169
174
|
end
|
170
175
|
# try cleaning up the browser again
|
@@ -246,13 +251,15 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
246
251
|
def accept_modal(_type, options={})
|
247
252
|
if headless_chrome?
|
248
253
|
raise ArgumentError, "Block that triggers the system modal is missing" unless block_given?
|
249
|
-
insert_modal_handlers(true, options[:with]
|
254
|
+
insert_modal_handlers(true, options[:with])
|
250
255
|
yield
|
251
256
|
find_headless_modal(options)
|
252
257
|
else
|
253
258
|
yield if block_given?
|
254
259
|
modal = find_modal(options)
|
260
|
+
|
255
261
|
modal.send_keys options[:with] if options[:with]
|
262
|
+
|
256
263
|
message = modal.text
|
257
264
|
modal.accept
|
258
265
|
message
|
@@ -262,7 +269,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
262
269
|
def dismiss_modal(_type, options={})
|
263
270
|
if headless_chrome?
|
264
271
|
raise ArgumentError, "Block that triggers the system modal is missing" unless block_given?
|
265
|
-
insert_modal_handlers(false, options[:with]
|
272
|
+
insert_modal_handlers(false, options[:with])
|
266
273
|
yield
|
267
274
|
find_headless_modal(options)
|
268
275
|
else
|
@@ -342,6 +349,14 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
342
349
|
options[:browser].to_s
|
343
350
|
end
|
344
351
|
|
352
|
+
def modal_error
|
353
|
+
if defined?(Selenium::WebDriver::Error::NoSuchAlertError)
|
354
|
+
Selenium::WebDriver::Error::NoSuchAlertError
|
355
|
+
else
|
356
|
+
Selenium::WebDriver::Error::NoAlertPresentError
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
345
360
|
def find_window(locator)
|
346
361
|
handles = browser.window_handles
|
347
362
|
return locator if handles.include? locator
|
@@ -359,7 +374,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
359
374
|
raise Capybara::ElementNotFound, "Could not find a window identified by #{locator}"
|
360
375
|
end
|
361
376
|
|
362
|
-
def insert_modal_handlers(accept, response_text
|
377
|
+
def insert_modal_handlers(accept, response_text)
|
363
378
|
script = <<-JS
|
364
379
|
if (typeof window.capybara === 'undefined') {
|
365
380
|
window.capybara = {
|
@@ -391,13 +406,13 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
391
406
|
}
|
392
407
|
window.capybara.add_handler(modal_handler);
|
393
408
|
|
394
|
-
window.alert = window.confirm = function(str) {
|
395
|
-
window.capybara.handler_called(modal_handler, str);
|
409
|
+
window.alert = window.confirm = function(str = "") {
|
410
|
+
window.capybara.handler_called(modal_handler, str.toString());
|
396
411
|
return #{accept ? 'true' : 'false'};
|
397
|
-
}
|
398
|
-
window.prompt = function(str) {
|
399
|
-
window.capybara.handler_called(modal_handler, str);
|
400
|
-
return #{accept ? "'#{response_text}'" : 'null'};
|
412
|
+
}
|
413
|
+
window.prompt = function(str = "", default_text = "") {
|
414
|
+
window.capybara.handler_called(modal_handler, str.toString());
|
415
|
+
return #{accept ? (response_text.nil? ? "default_text" : "'#{response_text}'") : 'null'};
|
401
416
|
}
|
402
417
|
JS
|
403
418
|
execute_script script
|
@@ -420,7 +435,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
420
435
|
# Actual wait time may be longer than specified
|
421
436
|
wait = Selenium::WebDriver::Wait.new(
|
422
437
|
timeout: options.fetch(:wait, session_options.default_max_wait_time) || 0 ,
|
423
|
-
ignore:
|
438
|
+
ignore: modal_error)
|
424
439
|
begin
|
425
440
|
wait.until do
|
426
441
|
alert = @browser.switch_to.alert
|
@@ -437,7 +452,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
437
452
|
# Actual wait time may be longer than specified
|
438
453
|
wait = Selenium::WebDriver::Wait.new(
|
439
454
|
timeout: options.fetch(:wait, session_options.default_max_wait_time) || 0 ,
|
440
|
-
ignore:
|
455
|
+
ignore: modal_error)
|
441
456
|
begin
|
442
457
|
wait.until do
|
443
458
|
called, alert_text = evaluate_script('window.capybara && window.capybara.current_modal_status()')
|
@@ -100,6 +100,15 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
100
100
|
|
101
101
|
def click
|
102
102
|
native.click
|
103
|
+
rescue => e
|
104
|
+
if e.is_a?(::Selenium::WebDriver::Error::ElementClickInterceptedError) ||
|
105
|
+
e.message =~ /Other element would receive the click/
|
106
|
+
begin
|
107
|
+
driver.execute_script("arguments[0].scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'})", self)
|
108
|
+
rescue
|
109
|
+
end
|
110
|
+
end
|
111
|
+
raise e
|
103
112
|
end
|
104
113
|
|
105
114
|
def right_click
|
data/lib/capybara/session.rb
CHANGED
@@ -642,6 +642,24 @@ module Capybara
|
|
642
642
|
element_script_result(result)
|
643
643
|
end
|
644
644
|
|
645
|
+
##
|
646
|
+
#
|
647
|
+
# Evaluate the given JavaScript and obtain the result from a callback function which will be passed as the last argument to the script.
|
648
|
+
#
|
649
|
+
# @param [String] script A string of JavaScript to evaluate
|
650
|
+
# @return [Object] The result of the evaluated JavaScript (may be driver specific)
|
651
|
+
#
|
652
|
+
def evaluate_async_script(script, *args)
|
653
|
+
@touched = true
|
654
|
+
result = if args.empty?
|
655
|
+
driver.evaluate_async_script(script)
|
656
|
+
else
|
657
|
+
raise Capybara::NotSupportedByDriverError, "The current driver does not support evaluate_async_script arguments" if driver.method(:evaluate_async_script).arity == 1
|
658
|
+
driver.evaluate_async_script(script, *args.map { |arg| arg.is_a?(Capybara::Node::Element) ? arg.base : arg} )
|
659
|
+
end
|
660
|
+
element_script_result(result)
|
661
|
+
end
|
662
|
+
|
645
663
|
##
|
646
664
|
#
|
647
665
|
# Execute the block, accepting a alert.
|
@@ -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,7 +24,9 @@ module Capybara
|
|
22
24
|
|
23
25
|
##
|
24
26
|
# Asserts that the page doesn't have the given path.
|
25
|
-
# By default this will compare against the
|
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
|
26
30
|
#
|
27
31
|
# @macro current_path_query_params
|
28
32
|
# @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
|
@@ -34,7 +38,9 @@ module Capybara
|
|
34
38
|
|
35
39
|
##
|
36
40
|
# Checks if the page has the given path.
|
37
|
-
# By default this will compare against the
|
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
|
38
44
|
#
|
39
45
|
# @macro current_path_query_params
|
40
46
|
# @return [Boolean]
|
@@ -47,7 +53,9 @@ module Capybara
|
|
47
53
|
|
48
54
|
##
|
49
55
|
# Checks if the page doesn't have the given path.
|
50
|
-
# By default this will compare against the
|
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
|
51
59
|
#
|
52
60
|
# @macro current_path_query_params
|
53
61
|
# @return [Boolean]
|
@@ -109,6 +109,14 @@ $(function() {
|
|
109
109
|
$(this).attr('response', response);
|
110
110
|
}
|
111
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
|
+
});
|
112
120
|
$('#open-twice').click(function() {
|
113
121
|
if (confirm('Are you sure?')) {
|
114
122
|
if (!confirm('Are you really sure?')) {
|
@@ -11,6 +11,13 @@ Capybara::SpecHelper.spec '#accept_prompt', requires: [:modals] do
|
|
11
11
|
expect(@session).to have_xpath("//a[@id='open-prompt' and @response='']")
|
12
12
|
end
|
13
13
|
|
14
|
+
it "should accept the prompt with no message when there is a default" do
|
15
|
+
@session.accept_prompt do
|
16
|
+
@session.click_link('Open defaulted prompt')
|
17
|
+
end
|
18
|
+
expect(@session).to have_xpath("//a[@id='open-prompt-with-default' and @response='Default value!']")
|
19
|
+
end
|
20
|
+
|
14
21
|
it "should return the message presented" do
|
15
22
|
message = @session.accept_prompt do
|
16
23
|
@session.click_link('Open prompt')
|
@@ -25,6 +32,21 @@ Capybara::SpecHelper.spec '#accept_prompt', requires: [:modals] do
|
|
25
32
|
expect(@session).to have_xpath("//a[@id='open-prompt' and @response='the response']")
|
26
33
|
end
|
27
34
|
|
35
|
+
it "should accept the prompt with a response when there is a default" do
|
36
|
+
@session.accept_prompt with: 'the response' do
|
37
|
+
@session.click_link('Open defaulted prompt')
|
38
|
+
end
|
39
|
+
expect(@session).to have_xpath("//a[@id='open-prompt-with-default' and @response='the response']")
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should accept the prompt with a blank response when there is a default" do
|
43
|
+
pending "Geckodriver doesn't set a blank response currently" if @session.respond_to?(:mode) && @session.mode.to_s == "selenium_marionette"
|
44
|
+
@session.accept_prompt with: '' do
|
45
|
+
@session.click_link('Open defaulted prompt')
|
46
|
+
end
|
47
|
+
expect(@session).to have_xpath("//a[@id='open-prompt-with-default' and @response='']")
|
48
|
+
end
|
49
|
+
|
28
50
|
it "should accept the prompt if the message matches" do
|
29
51
|
@session.accept_prompt 'Prompt opened', with: 'matched' do
|
30
52
|
@session.click_link('Open prompt')
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
Capybara::SpecHelper.spec "#evaluate_async_script", requires: [:js] do
|
3
|
+
it "should evaluate the given script and return whatever it produces" do
|
4
|
+
@session.visit('/with_js')
|
5
|
+
expect(@session.evaluate_async_script("arguments[0](4)")).to eq(4)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should support passing elements as arguments to the script", requires: [:js, :es_args] do
|
9
|
+
@session.visit('/with_js')
|
10
|
+
el = @session.find(:css, '#drag p')
|
11
|
+
result = @session.evaluate_async_script("arguments[2]([arguments[0].innerText, arguments[1]])", el, "Doodle Funk")
|
12
|
+
expect(result).to eq ["This is a draggable element.", "Doodle Funk"]
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should support returning elements after asynchronous operation", requires: [:js, :es_args] do
|
16
|
+
@session.visit('/with_js')
|
17
|
+
@session.find(:css, '#change') # ensure page has loaded and element is available
|
18
|
+
el = @session.evaluate_async_script("var cb = arguments[0]; setTimeout(function(){ cb(document.getElementById('change')) }, 100)")
|
19
|
+
expect(el).to be_instance_of(Capybara::Node::Element)
|
20
|
+
expect(el).to eq(@session.find(:css, '#change'))
|
21
|
+
end
|
22
|
+
end
|
@@ -20,7 +20,7 @@ Capybara::SpecHelper.spec "#evaluate_script", requires: [:js] do
|
|
20
20
|
|
21
21
|
it "should support returning elements", requires: [:js, :es_args] do
|
22
22
|
@session.visit('/with_js')
|
23
|
-
|
23
|
+
@session.find(:css, '#change') # ensure page has loaded and element is available
|
24
24
|
el = @session.evaluate_script("document.getElementById('change')")
|
25
25
|
expect(el).to be_instance_of(Capybara::Node::Element)
|
26
26
|
expect(el).to eq(@session.find(:css, '#change'))
|
@@ -37,20 +37,47 @@ Capybara::SpecHelper.spec '#has_current_path?' do
|
|
37
37
|
expect(@session).to have_current_path('/with_js?test=test')
|
38
38
|
end
|
39
39
|
|
40
|
-
it "should compare the full url" do
|
40
|
+
it "should compare the full url if url: true is used" do
|
41
41
|
expect(@session).to have_current_path(%r{\Ahttp://[^/]*/with_js\Z}, url: true)
|
42
|
+
domain_port = if @session.respond_to?(:server) && @session.server
|
43
|
+
"#{@session.server.host}:#{@session.server.port}"
|
44
|
+
else
|
45
|
+
"www.example.com"
|
46
|
+
end
|
47
|
+
expect(@session).to have_current_path("http://#{domain_port}/with_js", url: true)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should not compare the full url if url: true is not passed" do
|
51
|
+
expect(@session).to have_current_path(%r{^/with_js\Z})
|
52
|
+
expect(@session).to have_current_path('/with_js')
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should not compare the full url if url: false is passed" do
|
56
|
+
expect(@session).to have_current_path(%r{^/with_js\Z}, url: false)
|
57
|
+
expect(@session).to have_current_path('/with_js', url: false)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should default to full url if value is a url" do
|
61
|
+
url = @session.current_url
|
62
|
+
expect(url).to match /with_js$/
|
63
|
+
expect(@session).to have_current_path(url)
|
64
|
+
expect(@session).not_to have_current_path("http://www.not_example.com/with_js")
|
42
65
|
end
|
43
66
|
|
44
67
|
it "should ignore the query" do
|
45
68
|
@session.visit('/with_js?test=test')
|
46
69
|
expect(@session).to have_current_path('/with_js?test=test')
|
47
70
|
expect(@session).to have_current_path('/with_js', only_path: true)
|
71
|
+
expect(@session).to have_current_path('/with_js', ignore_query: true)
|
72
|
+
uri = ::Addressable::URI.parse(@session.current_url)
|
73
|
+
uri.query = nil
|
74
|
+
expect(@session).to have_current_path(uri.to_s, ignore_query: true)
|
48
75
|
end
|
49
76
|
|
50
77
|
it "should not allow url and only_path at the same time" do
|
51
78
|
expect {
|
52
79
|
expect(@session).to have_current_path('/with_js', url: true, only_path: true)
|
53
|
-
|
80
|
+
}.to raise_error ArgumentError
|
54
81
|
end
|
55
82
|
|
56
83
|
it "should not raise an exception if the current_url is nil" do
|
@@ -59,12 +86,17 @@ Capybara::SpecHelper.spec '#has_current_path?' do
|
|
59
86
|
# Without only_path option
|
60
87
|
expect {
|
61
88
|
expect(@session).to have_current_path(nil)
|
62
|
-
}.
|
89
|
+
}.not_to raise_exception
|
63
90
|
|
64
91
|
# With only_path option
|
65
92
|
expect {
|
66
93
|
expect(@session).to have_current_path(nil, only_path: true)
|
67
|
-
}.
|
94
|
+
}.not_to raise_exception
|
95
|
+
|
96
|
+
# With ignore_query option
|
97
|
+
expect {
|
98
|
+
expect(@session).to have_current_path(nil, ignore_query: true)
|
99
|
+
}.not_to raise_exception
|
68
100
|
end
|
69
101
|
end
|
70
102
|
|
@@ -105,5 +137,10 @@ Capybara::SpecHelper.spec '#has_no_current_path?' do
|
|
105
137
|
expect {
|
106
138
|
expect(@session).not_to have_current_path('/with_js', only_path: true)
|
107
139
|
}. not_to raise_exception
|
140
|
+
|
141
|
+
# With ignore_query option
|
142
|
+
expect {
|
143
|
+
expect(@session).not_to have_current_path('/with_js', ignore_query: true)
|
144
|
+
}. not_to raise_exception
|
108
145
|
end
|
109
146
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<style>
|
4
|
+
header { height: 45px; position: fixed; top: 0; background-color: red; width: 100%;}
|
5
|
+
footer { height: 45px; position: fixed; bottom: 0; background-color: red; width: 100%;}
|
6
|
+
#main { margin: 45px;}
|
7
|
+
#tall { display: block; height: 2000px;}
|
8
|
+
</style>
|
9
|
+
</head>
|
10
|
+
<body>
|
11
|
+
<header>My headers</header>
|
12
|
+
<div id="main">
|
13
|
+
<div id="tall">A tall block</div>
|
14
|
+
<a href="/">Go to root</a>
|
15
|
+
</div>
|
16
|
+
<footer>My footer</footer>
|
17
|
+
</body>
|
data/lib/capybara/version.rb
CHANGED
@@ -14,7 +14,7 @@ Capybara.register_driver :selenium_marionette do |app|
|
|
14
14
|
Capybara::Selenium::Driver.new(
|
15
15
|
app,
|
16
16
|
browser: :firefox,
|
17
|
-
desired_capabilities: {:
|
17
|
+
desired_capabilities: {marionette: true, 'moz:webdriverClick': true},
|
18
18
|
options: browser_options
|
19
19
|
# Get a trace level log from geckodriver
|
20
20
|
# :driver_opts => { args: ['-vv'] }
|
@@ -55,7 +55,7 @@ end
|
|
55
55
|
|
56
56
|
RSpec.describe Capybara::Selenium::Driver do
|
57
57
|
before do
|
58
|
-
@driver = Capybara::Selenium::Driver.new(TestApp, browser: :firefox)
|
58
|
+
@driver = Capybara::Selenium::Driver.new(TestApp, browser: :firefox, options: browser_options)
|
59
59
|
end
|
60
60
|
|
61
61
|
describe '#quit' do
|
@@ -61,7 +61,7 @@ RSpec.shared_examples "Capybara::Session" do |session, mode|
|
|
61
61
|
skip "Headless Chrome doesn't support blockless modal methods" if @session.driver.send(:headless_chrome?)
|
62
62
|
@session.click_link('Open alert')
|
63
63
|
@session.accept_alert
|
64
|
-
expect{@session.driver.browser.switch_to.alert}.to raise_error(
|
64
|
+
expect{@session.driver.browser.switch_to.alert}.to raise_error(@session.driver.send(:modal_error))
|
65
65
|
end
|
66
66
|
|
67
67
|
it "raises if block is missing" do
|
@@ -180,6 +180,17 @@ RSpec.shared_examples "Capybara::Session" do |session, mode|
|
|
180
180
|
}
|
181
181
|
})
|
182
182
|
end
|
183
|
+
|
184
|
+
describe "#evaluate_async_script" do
|
185
|
+
it "will timeout if the script takes too long" do
|
186
|
+
@session.visit('/with_js')
|
187
|
+
expect do
|
188
|
+
@session.using_wait_time(1) do
|
189
|
+
@session.evaluate_async_script("var cb = arguments[0]; setTimeout(function(){ cb(null) }, 3000)")
|
190
|
+
end
|
191
|
+
end.to raise_error Selenium::WebDriver::Error::ScriptTimeoutError
|
192
|
+
end
|
193
|
+
end
|
183
194
|
end
|
184
195
|
|
185
196
|
describe "Element#inspect" do
|
@@ -191,5 +202,17 @@ RSpec.shared_examples "Capybara::Session" do |session, mode|
|
|
191
202
|
expect(el.inspect).to eq "Obsolete #<Capybara::Node::Element>"
|
192
203
|
end
|
193
204
|
end
|
205
|
+
|
206
|
+
describe "Element#click" do
|
207
|
+
it "should handle fixed headers/footers" do
|
208
|
+
if mode == :selenium_marionette && !(ENV['TRAVIS_ALLOW_FAILURE']=='true')
|
209
|
+
pending "geckodriver/firefox/marionette just fail to click without reporting an error - no way for us to detect/catch"
|
210
|
+
end
|
211
|
+
@session.visit('/with_fixed_header_footer')
|
212
|
+
# @session.click_link('Go to root')
|
213
|
+
@session.find(:link, 'Go to root').click
|
214
|
+
expect(@session).to have_current_path('/')
|
215
|
+
end
|
216
|
+
end
|
194
217
|
end
|
195
218
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: capybara
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.16.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thomas Walpole
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain:
|
12
12
|
- gem-public_cert.pem
|
13
|
-
date: 2017-
|
13
|
+
date: 2017-11-13 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: nokogiri
|
@@ -376,6 +376,7 @@ files:
|
|
376
376
|
- lib/capybara/spec/session/element/match_css_spec.rb
|
377
377
|
- lib/capybara/spec/session/element/match_xpath_spec.rb
|
378
378
|
- lib/capybara/spec/session/element/matches_selector_spec.rb
|
379
|
+
- lib/capybara/spec/session/evaluate_async_script_spec.rb
|
379
380
|
- lib/capybara/spec/session/evaluate_script_spec.rb
|
380
381
|
- lib/capybara/spec/session/execute_script_spec.rb
|
381
382
|
- lib/capybara/spec/session/fill_in_spec.rb
|
@@ -447,6 +448,7 @@ files:
|
|
447
448
|
- lib/capybara/spec/views/tables.erb
|
448
449
|
- lib/capybara/spec/views/with_base_tag.erb
|
449
450
|
- lib/capybara/spec/views/with_count.erb
|
451
|
+
- lib/capybara/spec/views/with_fixed_header_footer.erb
|
450
452
|
- lib/capybara/spec/views/with_hover.erb
|
451
453
|
- lib/capybara/spec/views/with_html.erb
|
452
454
|
- lib/capybara/spec/views/with_html_entities.erb
|
@@ -506,7 +508,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
506
508
|
version: '0'
|
507
509
|
requirements: []
|
508
510
|
rubyforge_project:
|
509
|
-
rubygems_version: 2.
|
511
|
+
rubygems_version: 2.7.0
|
510
512
|
signing_key:
|
511
513
|
specification_version: 4
|
512
514
|
summary: Capybara aims to simplify the process of integration testing Rack applications,
|