capybara 3.5.1 → 3.6.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 +9 -0
- data/README.md +14 -2
- data/lib/capybara.rb +2 -0
- data/lib/capybara/driver/base.rb +7 -1
- data/lib/capybara/node/document.rb +2 -2
- data/lib/capybara/node/document_matchers.rb +2 -6
- data/lib/capybara/node/element.rb +3 -2
- data/lib/capybara/node/matchers.rb +20 -27
- data/lib/capybara/node/simple.rb +3 -2
- data/lib/capybara/queries/text_query.rb +9 -11
- data/lib/capybara/rack_test/driver.rb +0 -1
- data/lib/capybara/selenium/driver.rb +0 -1
- data/lib/capybara/selenium/node.rb +4 -5
- data/lib/capybara/selenium/nodes/chrome_node.rb +29 -0
- data/lib/capybara/selenium/nodes/marionette_node.rb +78 -2
- data/lib/capybara/session/config.rb +2 -1
- data/lib/capybara/session/matchers.rb +9 -6
- data/lib/capybara/spec/public/test.js +10 -0
- data/lib/capybara/spec/session/has_css_spec.rb +20 -0
- data/lib/capybara/spec/session/has_title_spec.rb +5 -0
- data/lib/capybara/spec/session/node_spec.rb +2 -2
- data/lib/capybara/spec/spec_helper.rb +1 -0
- data/lib/capybara/spec/views/with_js.erb +13 -0
- data/lib/capybara/version.rb +1 -1
- data/spec/selenium_spec_firefox_remote.rb +0 -4
- data/spec/selenium_spec_marionette.rb +0 -4
- data/spec/shared_selenium_session.rb +30 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1f61329ea428004635c5d6768afcdc61fcc22a2dd3a4cb0f14bde2d3b05102cb
|
4
|
+
data.tar.gz: d9d3de632455c5a3a8eecbef2c2fccc7b8d301bb589dcd7a5a21c719e639a5f5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 79e61c8494174a8006f158dc0b26d143a14525e4c0e77b4a1b5148f7f46c79207d4d5d89d0f5ef6f3ed3a8374bf2b54e915160770c6c254147c9aea52da48bf1
|
7
|
+
data.tar.gz: b329ae727a2c0d47903f9c992e8acd9e7e26e3bb280751fb8f681cb67c29650ad11d002ad9bfae627c1e767a00483e97a74667e564d4cec0bae2c529afc27452
|
data/History.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
# Version 3.6.0
|
2
|
+
Release date: 2018-08-14
|
3
|
+
|
4
|
+
### Added
|
5
|
+
|
6
|
+
* Workaround geckodriver/firefox send_keys issues as much as possible using the Selenium actions API
|
7
|
+
* Workaround lack of HTML5 native drag and drop events when using Selenium driver with Chrome and FF >= 62
|
8
|
+
* `Capybara.predicates_wait` option which sets whether or not Capybaras matcher predicate methods (`has_css?`, `has_selector?`, `has_text?`, etc.) default to using waiting/retrying behavior (defaults to true)
|
9
|
+
|
1
10
|
# Version 3.5.1
|
2
11
|
Release date: 2018-08-03
|
3
12
|
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
[](https://dependabot.com/compatibility-score.html?dependency-name=capybara&package-manager=bundler&version-scheme=semver)
|
8
8
|
|
9
9
|
**Note** You are viewing the README for the development version of Capybara. If you are using the current release version
|
10
|
-
you can find the README at https://github.com/teamcapybara/capybara/blob/3.
|
10
|
+
you can find the README at https://github.com/teamcapybara/capybara/blob/3.6_stable/README.md
|
11
11
|
|
12
12
|
|
13
13
|
Capybara helps you test web applications by simulating how a real user would
|
@@ -389,9 +389,20 @@ See the section on adding and configuring drivers.
|
|
389
389
|
|
390
390
|
Capybara supports [Selenium 3.5+
|
391
391
|
(Webdriver)](https://www.seleniumhq.org/projects/webdriver/).
|
392
|
-
In order to use Selenium, you'll need to install the`selenium-webdriver` gem,
|
392
|
+
In order to use Selenium, you'll need to install the `selenium-webdriver` gem,
|
393
393
|
and add it to your Gemfile if you're using bundler.
|
394
394
|
|
395
|
+
Capybara pre-registers a number of named drives that use Selenium - they are:
|
396
|
+
|
397
|
+
* :selenium => Selenium driving Firefox
|
398
|
+
* :selenium_chrome => Selenium driving Chrome
|
399
|
+
* :selenium_chrome_headless => Selenium driving Chrome in a headless configuration
|
400
|
+
|
401
|
+
These should work (with relevant software installation) in a local desktop configuration but you may
|
402
|
+
need to customize them if using in a CI environment where additional options may need to be passed
|
403
|
+
to the browsers. See the section on adding and configuring drivers.
|
404
|
+
|
405
|
+
|
395
406
|
**Note**: drivers which run the server in a different thread may not share the
|
396
407
|
same transaction as your tests, causing data not to be shared between your test
|
397
408
|
and test server, see "Transactions and database setup" below.
|
@@ -1000,6 +1011,7 @@ end
|
|
1000
1011
|
However, it's also possible to give this configuration a different name.
|
1001
1012
|
|
1002
1013
|
```ruby
|
1014
|
+
# Note: Capybara registers this by default
|
1003
1015
|
Capybara.register_driver :selenium_chrome do |app|
|
1004
1016
|
Capybara::Selenium::Driver.new(app, :browser => :chrome)
|
1005
1017
|
end
|
data/lib/capybara.rb
CHANGED
@@ -84,6 +84,7 @@ module Capybara
|
|
84
84
|
# [server = Symbol] The name of the registered server to use when running the app under test (Default: :webrick)
|
85
85
|
# [default_set_options = Hash] The default options passed to Node::set (Default: {})
|
86
86
|
# [test_id = Symbol/String/nil] Optional attribute to match locator aginst with builtin selectors along with id (Default: nil)
|
87
|
+
# [predicates_wait = Boolean] Whether Capybaras predicate matchers use waiting behavior by default (Default: true)
|
87
88
|
#
|
88
89
|
# === DSL Options
|
89
90
|
#
|
@@ -484,6 +485,7 @@ Capybara.configure do |config|
|
|
484
485
|
config.reuse_server = true
|
485
486
|
config.default_set_options = {}
|
486
487
|
config.test_id = nil
|
488
|
+
config.predicates_wait = true
|
487
489
|
end
|
488
490
|
|
489
491
|
Capybara.register_driver :rack_test do |app|
|
data/lib/capybara/driver/base.rb
CHANGED
@@ -20,8 +20,8 @@ module Capybara
|
|
20
20
|
#
|
21
21
|
# @return [String] The text of the document
|
22
22
|
#
|
23
|
-
def text(type = nil)
|
24
|
-
find(:xpath, '/html').text(type)
|
23
|
+
def text(type = nil, normalize_ws: false)
|
24
|
+
find(:xpath, '/html').text(type, normalize_ws: normalize_ws)
|
25
25
|
end
|
26
26
|
|
27
27
|
##
|
@@ -38,9 +38,7 @@ module Capybara
|
|
38
38
|
# @return [Boolean]
|
39
39
|
#
|
40
40
|
def has_title?(title, **options)
|
41
|
-
assert_title(title, options)
|
42
|
-
rescue Capybara::ExpectationNotMet
|
43
|
-
false
|
41
|
+
make_predicate(options) { assert_title(title, options) }
|
44
42
|
end
|
45
43
|
|
46
44
|
##
|
@@ -50,9 +48,7 @@ module Capybara
|
|
50
48
|
# @return [Boolean]
|
51
49
|
#
|
52
50
|
def has_no_title?(title, **options)
|
53
|
-
assert_no_title(title, options)
|
54
|
-
rescue Capybara::ExpectationNotMet
|
55
|
-
false
|
51
|
+
make_predicate(options) { assert_no_title(title, options) }
|
56
52
|
end
|
57
53
|
|
58
54
|
private
|
@@ -53,9 +53,10 @@ module Capybara
|
|
53
53
|
# @param type [:all, :visible] Whether to return only visible or all text
|
54
54
|
# @return [String] The text of the element
|
55
55
|
#
|
56
|
-
def text(type = nil)
|
56
|
+
def text(type = nil, normalize_ws: false)
|
57
57
|
type ||= :all unless session_options.ignore_hidden_elements || session_options.visible_text_only
|
58
|
-
synchronize { type == :all ? base.all_text : base.visible_text }
|
58
|
+
t = synchronize { type == :all ? base.all_text : base.visible_text }
|
59
|
+
normalize_ws ? t.gsub(/[[:space:]]+/, ' ').strip : t
|
59
60
|
end
|
60
61
|
|
61
62
|
##
|
@@ -36,10 +36,8 @@ module Capybara
|
|
36
36
|
# @option args [Range] :between (nil) Range of times that should contain number of times text occurs
|
37
37
|
# @return [Boolean] If the expression exists
|
38
38
|
#
|
39
|
-
def has_selector?(*args, &optional_filter_block)
|
40
|
-
assert_selector(*args, &optional_filter_block)
|
41
|
-
rescue Capybara::ExpectationNotMet
|
42
|
-
false
|
39
|
+
def has_selector?(*args, **options, &optional_filter_block)
|
40
|
+
make_predicate(options) { assert_selector(*args, options, &optional_filter_block) }
|
43
41
|
end
|
44
42
|
|
45
43
|
##
|
@@ -50,10 +48,8 @@ module Capybara
|
|
50
48
|
# @param (see Capybara::Node::Finders#has_selector?)
|
51
49
|
# @return [Boolean]
|
52
50
|
#
|
53
|
-
def has_no_selector?(*args, &optional_filter_block)
|
54
|
-
assert_no_selector(*args, &optional_filter_block)
|
55
|
-
rescue Capybara::ExpectationNotMet
|
56
|
-
false
|
51
|
+
def has_no_selector?(*args, **options, &optional_filter_block)
|
52
|
+
make_predicate(options) { assert_no_selector(*args, options, &optional_filter_block) }
|
57
53
|
end
|
58
54
|
|
59
55
|
##
|
@@ -66,9 +62,7 @@ module Capybara
|
|
66
62
|
# @return [Boolean] If the styles match
|
67
63
|
#
|
68
64
|
def has_style?(styles, **options)
|
69
|
-
assert_style(styles,
|
70
|
-
rescue Capybara::ExpectationNotMet
|
71
|
-
false
|
65
|
+
make_predicate(options) { assert_style(styles, options) }
|
72
66
|
end
|
73
67
|
|
74
68
|
##
|
@@ -554,10 +548,8 @@ module Capybara
|
|
554
548
|
# @param (see Capybara::Node::Finders#has_selector?)
|
555
549
|
# @return [Boolean]
|
556
550
|
#
|
557
|
-
def matches_selector?(*args, &optional_filter_block)
|
558
|
-
assert_matches_selector(*args, &optional_filter_block)
|
559
|
-
rescue Capybara::ExpectationNotMet
|
560
|
-
false
|
551
|
+
def matches_selector?(*args, **options, &optional_filter_block)
|
552
|
+
make_predicate(options) { assert_matches_selector(*args, options, &optional_filter_block) }
|
561
553
|
end
|
562
554
|
|
563
555
|
##
|
@@ -590,10 +582,8 @@ module Capybara
|
|
590
582
|
# @param (see Capybara::Node::Finders#has_selector?)
|
591
583
|
# @return [Boolean]
|
592
584
|
#
|
593
|
-
def not_matches_selector?(*args, &optional_filter_block)
|
594
|
-
assert_not_matches_selector(*args, &optional_filter_block)
|
595
|
-
rescue Capybara::ExpectationNotMet
|
596
|
-
false
|
585
|
+
def not_matches_selector?(*args, **options, &optional_filter_block)
|
586
|
+
make_predicate(options) { assert_not_matches_selector(*args, options, &optional_filter_block) }
|
597
587
|
end
|
598
588
|
|
599
589
|
##
|
@@ -683,10 +673,8 @@ module Capybara
|
|
683
673
|
# @macro text_query_params
|
684
674
|
# @return [Boolean] Whether it exists
|
685
675
|
#
|
686
|
-
def has_text?(*args)
|
687
|
-
assert_text(*args)
|
688
|
-
rescue Capybara::ExpectationNotMet
|
689
|
-
false
|
676
|
+
def has_text?(*args, **options)
|
677
|
+
make_predicate(options) { assert_text(*args, options) }
|
690
678
|
end
|
691
679
|
alias_method :has_content?, :has_text?
|
692
680
|
|
@@ -697,10 +685,8 @@ module Capybara
|
|
697
685
|
# @macro text_query_params
|
698
686
|
# @return [Boolean] Whether it doesn't exist
|
699
687
|
#
|
700
|
-
def has_no_text?(*args)
|
701
|
-
assert_no_text(*args)
|
702
|
-
rescue Capybara::ExpectationNotMet
|
703
|
-
false
|
688
|
+
def has_no_text?(*args, **options)
|
689
|
+
make_predicate(options) { assert_no_text(*args, options) }
|
704
690
|
end
|
705
691
|
alias_method :has_no_content?, :has_no_text?
|
706
692
|
|
@@ -745,6 +731,13 @@ module Capybara
|
|
745
731
|
query_options[:session_options] = session_options
|
746
732
|
query_args.push(query_options)
|
747
733
|
end
|
734
|
+
|
735
|
+
def make_predicate(options)
|
736
|
+
options[:wait] = 0 unless options.key?(:wait) || session_options.predicates_wait
|
737
|
+
yield
|
738
|
+
rescue Capybara::ExpectationNotMet
|
739
|
+
false
|
740
|
+
end
|
748
741
|
end
|
749
742
|
end
|
750
743
|
end
|
data/lib/capybara/node/simple.rb
CHANGED
@@ -5,12 +5,7 @@ module Capybara
|
|
5
5
|
module Queries
|
6
6
|
class TextQuery < BaseQuery
|
7
7
|
def initialize(type = nil, expected_text, session_options:, **options) # rubocop:disable Style/OptionalArguments
|
8
|
-
@type =
|
9
|
-
Capybara.ignore_hidden_elements || Capybara.visible_text_only ? :visible : :all
|
10
|
-
else
|
11
|
-
type
|
12
|
-
end
|
13
|
-
|
8
|
+
@type = type.nil? ? default_type : type
|
14
9
|
@expected_text = expected_text.is_a?(Regexp) ? expected_text : expected_text.to_s
|
15
10
|
@options = options
|
16
11
|
super(@options)
|
@@ -23,8 +18,7 @@ module Capybara
|
|
23
18
|
|
24
19
|
def resolve_for(node)
|
25
20
|
@node = node
|
26
|
-
@actual_text = text
|
27
|
-
@actual_text.gsub!(/[[:space:]]+/, ' ').strip! if options[:normalize_ws]
|
21
|
+
@actual_text = text
|
28
22
|
@count = @actual_text.scan(@search_regexp).size
|
29
23
|
end
|
30
24
|
|
@@ -74,7 +68,7 @@ module Capybara
|
|
74
68
|
end
|
75
69
|
|
76
70
|
def invisible_message
|
77
|
-
invisible_text = text(
|
71
|
+
invisible_text = text(query_type: :all)
|
78
72
|
invisible_count = invisible_text.scan(@search_regexp).size
|
79
73
|
return if invisible_count == @count
|
80
74
|
"it was found #{invisible_count} #{Capybara::Helpers.declension('time', 'times', invisible_count)} including non-visible text"
|
@@ -95,8 +89,12 @@ module Capybara
|
|
95
89
|
!@expected_text.is_a?(Regexp)
|
96
90
|
end
|
97
91
|
|
98
|
-
def text(node, query_type)
|
99
|
-
node.text(query_type)
|
92
|
+
def text(node: @node, query_type: @type)
|
93
|
+
node.text(query_type, normalize_ws: options[:normalize_ws])
|
94
|
+
end
|
95
|
+
|
96
|
+
def default_type
|
97
|
+
Capybara.ignore_hidden_elements || Capybara.visible_text_only ? :visible : :all
|
100
98
|
end
|
101
99
|
end
|
102
100
|
end
|
@@ -16,7 +16,6 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
16
16
|
|
17
17
|
def initialize(app, **options)
|
18
18
|
raise ArgumentError, 'rack-test requires a rack application, but none was given' unless app
|
19
|
-
@session = nil
|
20
19
|
@app = app
|
21
20
|
@options = DEFAULT_OPTIONS.merge(options)
|
22
21
|
end
|
@@ -47,7 +47,6 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
47
47
|
# Array => an array of keys to send before the value being set, e.g. [[:command, 'a'], :backspace]
|
48
48
|
def set(value, **options)
|
49
49
|
raise ArgumentError, "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}" if value.is_a?(Array) && !multiple?
|
50
|
-
|
51
50
|
case tag_name
|
52
51
|
when 'input'
|
53
52
|
case self[:type]
|
@@ -212,17 +211,17 @@ private
|
|
212
211
|
elsif clear == :backspace
|
213
212
|
# Clear field by sending the correct number of backspace keys.
|
214
213
|
backspaces = [:backspace] * self.value.to_s.length
|
215
|
-
|
214
|
+
send_keys(*([:end] + backspaces + [value.to_s]))
|
216
215
|
elsif clear == :none
|
217
|
-
|
216
|
+
send_keys(value.to_s)
|
218
217
|
elsif clear.is_a? Array
|
219
|
-
|
218
|
+
send_keys(*clear, value.to_s)
|
220
219
|
else
|
221
220
|
# Clear field by JavaScript assignment of the value property.
|
222
221
|
# Script can change a readonly element which user input cannot, so
|
223
222
|
# don't execute if readonly.
|
224
223
|
driver.execute_script "arguments[0].value = ''", self
|
225
|
-
|
224
|
+
send_keys(value.to_s)
|
226
225
|
end
|
227
226
|
end
|
228
227
|
|
@@ -10,9 +10,38 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
|
|
10
10
|
raise
|
11
11
|
end
|
12
12
|
|
13
|
+
def drag_to(element)
|
14
|
+
return super unless self[:draggable] == 'true'
|
15
|
+
|
16
|
+
scroll_if_needed { driver.browser.action.click_and_hold(native).perform }
|
17
|
+
driver.execute_script HTML5_DRAG_DROP_SCRIPT, self, element
|
18
|
+
end
|
19
|
+
|
13
20
|
private
|
14
21
|
|
15
22
|
def bridge
|
16
23
|
driver.browser.send(:bridge)
|
17
24
|
end
|
25
|
+
|
26
|
+
HTML5_DRAG_DROP_SCRIPT = <<~JS
|
27
|
+
var source = arguments[0];
|
28
|
+
var target = arguments[1];
|
29
|
+
|
30
|
+
var dt = new DataTransfer();
|
31
|
+
var opts = { cancelable: true, bubbles: true, dataTransfer: dt };
|
32
|
+
|
33
|
+
var dragEvent = new DragEvent('dragstart', opts);
|
34
|
+
source.dispatchEvent(dragEvent);
|
35
|
+
target.scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'});
|
36
|
+
var dragOverEvent = new DragEvent('dragover', opts);
|
37
|
+
target.dispatchEvent(dragOverEvent);
|
38
|
+
var dragLeaveEvent = new DragEvent('dragleave', opts);
|
39
|
+
target.dispatchEvent(dragLeaveEvent);
|
40
|
+
if (dragOverEvent.defaultPrevented) {
|
41
|
+
var dropEvent = new DragEvent('drop', opts);
|
42
|
+
target.dispatchEvent(dropEvent);
|
43
|
+
}
|
44
|
+
var dragEndEvent = new DragEvent('dragend', opts);
|
45
|
+
source.dispatchEvent(dragEndEvent);
|
46
|
+
JS
|
18
47
|
end
|
@@ -14,7 +14,7 @@ class Capybara::Selenium::MarionetteNode < Capybara::Selenium::Node
|
|
14
14
|
|
15
15
|
def disabled?
|
16
16
|
# Not sure exactly what version of FF fixed the below issue, but it is definitely fixed in 61+
|
17
|
-
return super unless
|
17
|
+
return super unless browser_version < 61.0
|
18
18
|
|
19
19
|
return true if super
|
20
20
|
# workaround for selenium-webdriver/geckodriver reporting elements as enabled when they are nested in disabling elements
|
@@ -27,7 +27,7 @@ class Capybara::Selenium::MarionetteNode < Capybara::Selenium::Node
|
|
27
27
|
|
28
28
|
def set_file(value) # rubocop:disable Naming/AccessorMethodName
|
29
29
|
native.clear # By default files are appended so we have to clear here
|
30
|
-
return super if
|
30
|
+
return super if browser_version >= 62.0
|
31
31
|
|
32
32
|
# Workaround lack of support for multiple upload by uploading one at a time
|
33
33
|
path_names = value.to_s.empty? ? [] : value
|
@@ -42,8 +42,58 @@ class Capybara::Selenium::MarionetteNode < Capybara::Selenium::Node
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
+
def send_keys(*args)
|
46
|
+
# https://github.com/mozilla/geckodriver/issues/846
|
47
|
+
return super(*args.map { |arg| arg == :space ? ' ' : arg }) if args.none? { |s| s.is_a? Array }
|
48
|
+
|
49
|
+
native.click
|
50
|
+
actions = driver.browser.action
|
51
|
+
args.each do |keys|
|
52
|
+
_send_keys(keys, actions)
|
53
|
+
end
|
54
|
+
actions.perform
|
55
|
+
end
|
56
|
+
|
57
|
+
def drag_to(element)
|
58
|
+
return super unless (browser_version >= 62.0) && (self[:draggable] == 'true')
|
59
|
+
|
60
|
+
scroll_if_needed { driver.browser.action.click_and_hold(native).perform }
|
61
|
+
driver.execute_script HTML5_DRAG_DROP_SCRIPT, self, element
|
62
|
+
end
|
63
|
+
|
45
64
|
private
|
46
65
|
|
66
|
+
def _send_keys(keys, actions, down_keys = nil)
|
67
|
+
case keys
|
68
|
+
when String
|
69
|
+
keys = keys.upcase if down_keys&.include?(:shift) # https://bugzilla.mozilla.org/show_bug.cgi?id=1405370
|
70
|
+
actions.send_keys(keys)
|
71
|
+
when :space
|
72
|
+
actions.send_keys(' ') # https://github.com/mozilla/geckodriver/issues/846
|
73
|
+
when :control, :left_control, :right_control,
|
74
|
+
:alt, :left_alt, :right_alt,
|
75
|
+
:shift, :left_shift, :right_shift,
|
76
|
+
:meta, :left_meta, :right_meta,
|
77
|
+
:command
|
78
|
+
if down_keys.nil?
|
79
|
+
actions.send_keys(keys)
|
80
|
+
else
|
81
|
+
down_keys << keys
|
82
|
+
actions.key_down(keys)
|
83
|
+
end
|
84
|
+
when Symbol
|
85
|
+
actions.send_keys(keys)
|
86
|
+
when Array
|
87
|
+
local_down_keys = []
|
88
|
+
keys.each do |sub_keys|
|
89
|
+
_send_keys(sub_keys, actions, local_down_keys)
|
90
|
+
end
|
91
|
+
local_down_keys.each { |key| actions.key_up(key) }
|
92
|
+
else
|
93
|
+
raise ArgumentError, 'Unknown keys type'
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
47
97
|
def bridge
|
48
98
|
driver.browser.send(:bridge)
|
49
99
|
end
|
@@ -56,4 +106,30 @@ private
|
|
56
106
|
result = bridge.http.call(:post, "session/#{bridge.session_id}/file", file: Selenium::WebDriver::Zipper.zip_file(local_file))
|
57
107
|
result['value']
|
58
108
|
end
|
109
|
+
|
110
|
+
def browser_version
|
111
|
+
driver.browser.capabilities[:browser_version].to_f
|
112
|
+
end
|
113
|
+
|
114
|
+
HTML5_DRAG_DROP_SCRIPT = <<~JS
|
115
|
+
var source = arguments[0];
|
116
|
+
var target = arguments[1];
|
117
|
+
|
118
|
+
var dt = new DataTransfer();
|
119
|
+
var opts = { cancelable: true, bubbles: true, dataTransfer: dt };
|
120
|
+
|
121
|
+
var dragEvent = new DragEvent('dragstart', opts);
|
122
|
+
source.dispatchEvent(dragEvent);
|
123
|
+
target.scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'});
|
124
|
+
var dragOverEvent = new DragEvent('dragover', opts);
|
125
|
+
target.dispatchEvent(dragOverEvent);
|
126
|
+
var dragLeaveEvent = new DragEvent('dragleave', opts);
|
127
|
+
target.dispatchEvent(dragLeaveEvent);
|
128
|
+
if (dragOverEvent.defaultPrevented) {
|
129
|
+
var dropEvent = new DragEvent('drop', opts);
|
130
|
+
target.dispatchEvent(dropEvent);
|
131
|
+
}
|
132
|
+
var dragEndEvent = new DragEvent('dragend', opts);
|
133
|
+
source.dispatchEvent(dragEndEvent);
|
134
|
+
JS
|
59
135
|
end
|
@@ -7,7 +7,8 @@ module Capybara
|
|
7
7
|
OPTIONS = %i[always_include_port run_server default_selector default_max_wait_time ignore_hidden_elements
|
8
8
|
automatic_reload match exact exact_text raise_server_errors visible_text_only
|
9
9
|
automatic_label_click enable_aria_label save_path asset_host default_host app_host
|
10
|
-
server_host server_port server_errors default_set_options disable_animation test_id
|
10
|
+
server_host server_port server_errors default_set_options disable_animation test_id
|
11
|
+
predicates_wait].freeze
|
11
12
|
|
12
13
|
attr_accessor(*OPTIONS)
|
13
14
|
|
@@ -47,9 +47,7 @@ module Capybara
|
|
47
47
|
# @return [Boolean]
|
48
48
|
#
|
49
49
|
def has_current_path?(path, **options)
|
50
|
-
assert_current_path(path, options)
|
51
|
-
rescue Capybara::ExpectationNotMet
|
52
|
-
false
|
50
|
+
make_predicate(options) { assert_current_path(path, options) }
|
53
51
|
end
|
54
52
|
|
55
53
|
##
|
@@ -62,9 +60,7 @@ module Capybara
|
|
62
60
|
# @return [Boolean]
|
63
61
|
#
|
64
62
|
def has_no_current_path?(path, **options)
|
65
|
-
assert_no_current_path(path, options)
|
66
|
-
rescue Capybara::ExpectationNotMet
|
67
|
-
false
|
63
|
+
make_predicate(options) { assert_no_current_path(path, options) }
|
68
64
|
end
|
69
65
|
|
70
66
|
private
|
@@ -76,5 +72,12 @@ module Capybara
|
|
76
72
|
end
|
77
73
|
true
|
78
74
|
end
|
75
|
+
|
76
|
+
def make_predicate(options)
|
77
|
+
options[:wait] = 0 unless options.key?(:wait) || config.predicates_wait
|
78
|
+
yield
|
79
|
+
rescue Capybara::ExpectationNotMet
|
80
|
+
false
|
81
|
+
end
|
79
82
|
end
|
80
83
|
end
|
@@ -8,6 +8,16 @@ $(function() {
|
|
8
8
|
$(this).html('Dropped!');
|
9
9
|
}
|
10
10
|
});
|
11
|
+
$('#drag_html5, #drag_html5_scroll').on('dragstart', function(ev){
|
12
|
+
ev.originalEvent.dataTransfer.setData("text", ev.target.id);
|
13
|
+
});
|
14
|
+
$('#drop_html5, #drop_html5_scroll').on('dragover', function(ev){
|
15
|
+
if ($(this).hasClass('drop')) { ev.preventDefault(); }
|
16
|
+
});
|
17
|
+
$('#drop_html5, #drop_html5_scroll').on('drop', function(ev){
|
18
|
+
ev.preventDefault();
|
19
|
+
$(this).html('HTML5 Dropped ' + ev.originalEvent.dataTransfer.getData("text"));
|
20
|
+
});
|
11
21
|
$('#clickable').click(function(e) {
|
12
22
|
var link = $(this);
|
13
23
|
setTimeout(function() {
|
@@ -30,6 +30,26 @@ Capybara::SpecHelper.spec '#has_css?' do
|
|
30
30
|
expect(@session).to have_css("input[type='submit'][value='New Here']")
|
31
31
|
end
|
32
32
|
|
33
|
+
context 'with predicates_wait == true' do
|
34
|
+
it 'should wait for content to appear', requires: [:js] do
|
35
|
+
Capybara.predicates_wait = true
|
36
|
+
Capybara.default_max_wait_time = 2
|
37
|
+
@session.visit('/with_js')
|
38
|
+
@session.click_link('Click me')
|
39
|
+
expect(@session.has_css?("input[type='submit'][value='New Here']")).to be true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'with predicates_wait == false' do
|
44
|
+
it 'should not wait for content to appear', requires: [:js] do
|
45
|
+
Capybara.predicates_wait = false
|
46
|
+
Capybara.default_max_wait_time = 2
|
47
|
+
@session.visit('/with_js')
|
48
|
+
@session.click_link('Click me')
|
49
|
+
expect(@session.has_css?("input[type='submit'][value='New Here']")).to be false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
33
53
|
context 'with between' do
|
34
54
|
it 'should be true if the content occurs within the range given' do
|
35
55
|
expect(@session).to have_css('p', between: 1..4)
|
@@ -7,6 +7,7 @@ Capybara::SpecHelper.spec '#has_title?' do
|
|
7
7
|
|
8
8
|
it 'should be true if the page has the given title' do
|
9
9
|
expect(@session).to have_title('with_js')
|
10
|
+
expect(@session.has_title?('with_js')).to be true
|
10
11
|
end
|
11
12
|
|
12
13
|
it 'should allow regexp matches' do
|
@@ -21,6 +22,7 @@ Capybara::SpecHelper.spec '#has_title?' do
|
|
21
22
|
|
22
23
|
it 'should be false if the page has not the given title' do
|
23
24
|
expect(@session).not_to have_title('monkey')
|
25
|
+
expect(@session.has_title?('monkey')).to be false
|
24
26
|
end
|
25
27
|
|
26
28
|
it 'should default to exact: false matching' do
|
@@ -31,6 +33,8 @@ Capybara::SpecHelper.spec '#has_title?' do
|
|
31
33
|
it 'should match exactly if exact: true option passed' do
|
32
34
|
expect(@session).to have_title('with_js', exact: true)
|
33
35
|
expect(@session).not_to have_title('with_', exact: true)
|
36
|
+
expect(@session.has_title?('with_js', exact: true)).to be true
|
37
|
+
expect(@session.has_title?('with_', exact: true)).to be false
|
34
38
|
end
|
35
39
|
|
36
40
|
it 'should match partial if exact: false option passed' do
|
@@ -62,5 +66,6 @@ Capybara::SpecHelper.spec '#has_no_title?' do
|
|
62
66
|
|
63
67
|
it 'should be true if the page has not the given title' do
|
64
68
|
expect(@session).to have_no_title('monkey')
|
69
|
+
expect(@session.has_no_title?('monkey')).to be true
|
65
70
|
end
|
66
71
|
end
|
@@ -306,7 +306,7 @@ Capybara::SpecHelper.spec 'node' do
|
|
306
306
|
element = @session.find('//div[@id="drag"]')
|
307
307
|
target = @session.find('//div[@id="drop"]')
|
308
308
|
element.drag_to(target)
|
309
|
-
expect(@session.
|
309
|
+
expect(@session).to have_xpath('//div[contains(., "Dropped!")]')
|
310
310
|
end
|
311
311
|
|
312
312
|
it 'should drag and drop if scrolling is needed' do
|
@@ -314,7 +314,7 @@ Capybara::SpecHelper.spec 'node' do
|
|
314
314
|
element = @session.find('//div[@id="drag_scroll"]')
|
315
315
|
target = @session.find('//div[@id="drop_scroll"]')
|
316
316
|
element.drag_to(target)
|
317
|
-
expect(@session.
|
317
|
+
expect(@session).to have_xpath('//div[contains(., "Dropped!")]')
|
318
318
|
end
|
319
319
|
end
|
320
320
|
|
@@ -19,6 +19,16 @@
|
|
19
19
|
<p>It should be dropped here.</p>
|
20
20
|
</div>
|
21
21
|
|
22
|
+
<div id="drop_html5_scroll" class="drop">
|
23
|
+
<p>It should be dropped here.</p>
|
24
|
+
</div>
|
25
|
+
<div id="drag_html5" draggable="true">
|
26
|
+
<p>This is an HTML5 draggable element.</p>
|
27
|
+
</div>
|
28
|
+
<div id="drop_html5" class="drop">
|
29
|
+
<p>It should be dropped here.</p>
|
30
|
+
</div>
|
31
|
+
|
22
32
|
<p><a href="#" id="clickable">Click me</a></p>
|
23
33
|
<p><a href="#" id="slow-click">Slowly</a></p>
|
24
34
|
|
@@ -135,6 +145,9 @@
|
|
135
145
|
<div id="drop_scroll">
|
136
146
|
<p>It should be dropped here.</p>
|
137
147
|
</div>
|
148
|
+
<div id="drag_html5_scroll" draggable="true">
|
149
|
+
<p>This is an HTML5 draggable element.</p>
|
150
|
+
</div>
|
138
151
|
|
139
152
|
<script type="text/javascript">
|
140
153
|
// a javascript comment
|
data/lib/capybara/version.rb
CHANGED
@@ -60,10 +60,6 @@ skipped_tests << :windows if ENV['TRAVIS'] && (ENV['SKIP_WINDOW'] || ENV['HEADLE
|
|
60
60
|
|
61
61
|
Capybara::SpecHelper.run_specs TestSessions::RemoteFirefox, FIREFOX_REMOTE_DRIVER.to_s, capybara_skip: skipped_tests do |example|
|
62
62
|
case example.metadata[:full_description]
|
63
|
-
when 'Capybara::Session selenium_firefox_remote node #send_keys should generate key events',
|
64
|
-
'Capybara::Session selenium_firefox_remote node #send_keys should allow for multiple simultaneous keys',
|
65
|
-
'Capybara::Session selenium_firefox_remote node #send_keys should send special characters'
|
66
|
-
pending "selenium-webdriver/geckodriver doesn't support complex sets of characters"
|
67
63
|
when 'Capybara::Session selenium_firefox_remote node #click should allow multiple modifiers'
|
68
64
|
pending "Firefox doesn't generate an event for shift+control+click" if marionette_gte?(62, @session)
|
69
65
|
when /^Capybara::Session selenium node #double_click/
|
@@ -50,10 +50,6 @@ $stdout.puts `#{Selenium::WebDriver::Firefox.driver_path} --version` if ENV['CI'
|
|
50
50
|
|
51
51
|
Capybara::SpecHelper.run_specs TestSessions::SeleniumMarionette, 'selenium', capybara_skip: skipped_tests do |example|
|
52
52
|
case example.metadata[:full_description]
|
53
|
-
when 'Capybara::Session selenium node #send_keys should generate key events',
|
54
|
-
'Capybara::Session selenium node #send_keys should allow for multiple simultaneous keys',
|
55
|
-
'Capybara::Session selenium node #send_keys should send special characters'
|
56
|
-
pending "selenium-webdriver/geckodriver doesn't support complex sets of characters"
|
57
53
|
when 'Capybara::Session selenium node #click should allow multiple modifiers'
|
58
54
|
pending "Firefox doesn't generate an event for shift+control+click" if marionette_gte?(62, @session)
|
59
55
|
when /^Capybara::Session selenium node #double_click/
|
@@ -185,7 +185,6 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
|
|
185
185
|
|
186
186
|
context '#fill_in with { clear: Array } fill_options' do
|
187
187
|
it 'should pass the array through to the element' do
|
188
|
-
pending "selenium-webdriver/geckodriver doesn't support complex sets of characters" if marionette?(session)
|
189
188
|
# this is mainly for use with [[:control, 'a'], :backspace] - however since that is platform dependant I'm testing with something less useful
|
190
189
|
session.visit('/form')
|
191
190
|
session.fill_in('form_first_name',
|
@@ -286,6 +285,36 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
|
|
286
285
|
end
|
287
286
|
end
|
288
287
|
|
288
|
+
describe 'Element#drag_to' do
|
289
|
+
it 'should HTML5 drag and drop an object' do
|
290
|
+
pending "Firefox < 62 doesn't support a DataTransfer constuctor" if marionette_lt?(62.0, session)
|
291
|
+
session.visit('/with_js')
|
292
|
+
element = session.find('//div[@id="drag_html5"]')
|
293
|
+
target = session.find('//div[@id="drop_html5"]')
|
294
|
+
element.drag_to(target)
|
295
|
+
expect(session).to have_xpath('//div[contains(., "HTML5 Dropped drag_html5")]')
|
296
|
+
end
|
297
|
+
|
298
|
+
it 'should not HTML5 drag and drop on a non HTML5 drop element' do
|
299
|
+
session.visit('/with_js')
|
300
|
+
element = session.find('//div[@id="drag_html5"]')
|
301
|
+
target = session.find('//div[@id="drop_html5"]')
|
302
|
+
target.execute_script("$(this).removeClass('drop');")
|
303
|
+
element.drag_to(target)
|
304
|
+
sleep 1
|
305
|
+
expect(session).not_to have_xpath('//div[contains(., "HTML5 Dropped drag_html5")]')
|
306
|
+
end
|
307
|
+
|
308
|
+
it 'should HTML6 drag and drop when scrolling needed' do
|
309
|
+
pending "Firefox < 62 doesn't support a DataTransfer constuctor" if marionette_lt?(62.0, session)
|
310
|
+
session.visit('/with_js')
|
311
|
+
element = session.find('//div[@id="drag_html5_scroll"]')
|
312
|
+
target = session.find('//div[@id="drop_html5_scroll"]')
|
313
|
+
element.drag_to(target)
|
314
|
+
expect(session).to have_xpath('//div[contains(., "HTML5 Dropped drag_html5_scroll")]')
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
289
318
|
context 'Windows' do
|
290
319
|
it "can't close the primary window" do
|
291
320
|
expect do
|
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: 3.
|
4
|
+
version: 3.6.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: 2018-08-
|
13
|
+
date: 2018-08-14 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: addressable
|
@@ -503,7 +503,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
503
503
|
version: '0'
|
504
504
|
requirements: []
|
505
505
|
rubyforge_project:
|
506
|
-
rubygems_version: 2.7.
|
506
|
+
rubygems_version: 2.7.7
|
507
507
|
signing_key:
|
508
508
|
specification_version: 4
|
509
509
|
summary: Capybara aims to simplify the process of integration testing Rack applications,
|