capybara 3.5.1 → 3.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3c416e36d54b64d622c2a65481f5f60b3ca81618e8b8288accebe3d1aca55083
4
- data.tar.gz: dc271c3d46f477b3b9930647a55ec826bd477bc5c3b371fa39b3e02bbc916284
3
+ metadata.gz: 1f61329ea428004635c5d6768afcdc61fcc22a2dd3a4cb0f14bde2d3b05102cb
4
+ data.tar.gz: d9d3de632455c5a3a8eecbef2c2fccc7b8d301bb589dcd7a5a21c719e639a5f5
5
5
  SHA512:
6
- metadata.gz: c7e961f7131054a93ba9213e9a258f163e99f5a9484e6ba6ea1a2868ac0db435967e64bab16d382d5db04b6a485582c05da82bf4fb94b9f6403ca333b3c82a27
7
- data.tar.gz: f8b5c7a34e33813e687ba31a9e4eee74ede985e725d1755093eab20a0d3a30e6ceb7cb05529403cb42ea3e0b77cea52db3b9b43ab61c697ba2e57a312dd5f49b
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
  [![SemVer](https://api.dependabot.com/badges/compatibility_score?dependency-name=capybara&package-manager=bundler&version-scheme=semver)](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.5_stable/README.md
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
@@ -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|
@@ -159,6 +159,12 @@ class Capybara::Driver::Base
159
159
  end
160
160
 
161
161
  def session_options
162
- @session&.config || Capybara.session_options
162
+ session&.config || Capybara.session_options
163
+ end
164
+
165
+ private
166
+
167
+ def session
168
+ @session ||= nil
163
169
  end
164
170
  end
@@ -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, **options)
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
@@ -28,8 +28,9 @@ module Capybara
28
28
  #
29
29
  # @return [String] The text of the element
30
30
  #
31
- def text(_type = nil)
32
- native.text
31
+ def text(_type = nil, normalize_ws: false)
32
+ t = native.text
33
+ normalize_ws ? t.gsub(/[[:space:]]+/, ' ').strip : t
33
34
  end
34
35
 
35
36
  ##
@@ -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 = if type.nil?
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(node, @type)
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(@node, :all)
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
@@ -46,7 +46,6 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
46
46
 
47
47
  def initialize(app, **options)
48
48
  self.class.load_selenium
49
- @session = nil
50
49
  @app = app
51
50
  @browser = nil
52
51
  @exit_status = nil
@@ -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
- native.send_keys(*([:end] + backspaces + [value.to_s]))
214
+ send_keys(*([:end] + backspaces + [value.to_s]))
216
215
  elsif clear == :none
217
- native.send_keys(value.to_s)
216
+ send_keys(value.to_s)
218
217
  elsif clear.is_a? Array
219
- native.send_keys(*clear, value.to_s)
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
- native.send_keys(value.to_s)
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 driver.browser.capabilities[:browser_version].to_f < 61.0
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 driver.browser.capabilities[:browser_version].to_f >= 62.0
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].freeze
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.find('//div[contains(., "Dropped!")]')).not_to be_nil
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.find('//div[contains(., "Dropped!")]')).not_to be_nil
317
+ expect(@session).to have_xpath('//div[contains(., "Dropped!")]')
318
318
  end
319
319
  end
320
320
 
@@ -34,6 +34,7 @@ module Capybara
34
34
  Capybara.default_set_options = {}
35
35
  Capybara.disable_animation = false
36
36
  Capybara.test_id = nil
37
+ Capybara.predicates_wait = true
37
38
  reset_threadsafe
38
39
  end
39
40
 
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Capybara
4
- VERSION = '3.5.1'
4
+ VERSION = '3.6.0'
5
5
  end
@@ -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.5.1
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-03 00:00:00.000000000 Z
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.6
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,