capybara 3.20.0 → 3.21.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 +29 -0
- data/README.md +4 -4
- data/lib/capybara/driver/node.rb +4 -0
- data/lib/capybara/helpers.rb +1 -1
- data/lib/capybara/minitest.rb +4 -4
- data/lib/capybara/node/actions.rb +36 -29
- data/lib/capybara/node/element.rb +123 -82
- data/lib/capybara/node/finders.rb +2 -7
- data/lib/capybara/node/simple.rb +1 -0
- data/lib/capybara/selector/definition/css.rb +4 -1
- data/lib/capybara/selector/regexp_disassembler.rb +2 -2
- data/lib/capybara/selenium/extensions/html5_drag.rb +63 -0
- data/lib/capybara/selenium/node.rb +4 -0
- data/lib/capybara/selenium/nodes/chrome_node.rb +4 -0
- data/lib/capybara/selenium/nodes/firefox_node.rb +4 -13
- data/lib/capybara/selenium/nodes/ie_node.rb +14 -3
- data/lib/capybara/selenium/nodes/safari_node.rb +0 -13
- data/lib/capybara/spec/public/test.js +29 -3
- data/lib/capybara/spec/session/attach_file_spec.rb +13 -5
- data/lib/capybara/spec/session/has_css_spec.rb +4 -3
- data/lib/capybara/spec/session/node_spec.rb +86 -0
- data/lib/capybara/spec/session/scroll_spec.rb +1 -1
- data/lib/capybara/spec/spec_helper.rb +4 -0
- data/lib/capybara/spec/views/with_html.erb +4 -0
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara.rb +19 -11
- data/spec/selenium_spec_firefox.rb +2 -0
- data/spec/selenium_spec_ie.rb +13 -7
- data/spec/selenium_spec_safari.rb +2 -0
- data/spec/shared_selenium_session.rb +1 -51
- metadata +18 -18
|
@@ -43,7 +43,7 @@ module Capybara
|
|
|
43
43
|
|
|
44
44
|
##
|
|
45
45
|
#
|
|
46
|
-
# Retrieve the text of the element. If
|
|
46
|
+
# Retrieve the text of the element. If {Capybara.configure ignore_hidden_elements}
|
|
47
47
|
# is `true`, which it is by default, then this will return only text
|
|
48
48
|
# which is visible. The exact semantics of this may differ between
|
|
49
49
|
# drivers, but generally any text within elements with `display:none` is
|
|
@@ -61,7 +61,7 @@ module Capybara
|
|
|
61
61
|
|
|
62
62
|
##
|
|
63
63
|
#
|
|
64
|
-
# Retrieve the given attribute
|
|
64
|
+
# Retrieve the given attribute.
|
|
65
65
|
#
|
|
66
66
|
# element[:title] # => HTML title attribute
|
|
67
67
|
#
|
|
@@ -74,7 +74,7 @@ module Capybara
|
|
|
74
74
|
|
|
75
75
|
##
|
|
76
76
|
#
|
|
77
|
-
# Retrieve the given CSS styles
|
|
77
|
+
# Retrieve the given CSS styles.
|
|
78
78
|
#
|
|
79
79
|
# element.style('color', 'font-size') # => Computed values of CSS 'color' and 'font-size' styles
|
|
80
80
|
#
|
|
@@ -109,7 +109,7 @@ module Capybara
|
|
|
109
109
|
# Set the value of the form element to the given value.
|
|
110
110
|
#
|
|
111
111
|
# @param [String] value The new value
|
|
112
|
-
# @param [Hash
|
|
112
|
+
# @param [Hash] options Driver specific options for how to set the value. Take default values from {Capybara.configure default_set_options}.
|
|
113
113
|
#
|
|
114
114
|
# @return [Capybara::Node::Element] The element
|
|
115
115
|
def set(value, **options)
|
|
@@ -122,8 +122,15 @@ module Capybara
|
|
|
122
122
|
|
|
123
123
|
##
|
|
124
124
|
#
|
|
125
|
-
# Select this node if is an option element inside a select tag
|
|
125
|
+
# Select this node if it is an option element inside a select tag.
|
|
126
126
|
#
|
|
127
|
+
# @!macro action_waiting_behavior
|
|
128
|
+
# If the driver dynamic pages (JS) and the element is currently non-interactable, this method will
|
|
129
|
+
# continuously retry the action until either the element becomes interactable or the maximum
|
|
130
|
+
# wait time expires.
|
|
131
|
+
#
|
|
132
|
+
# @param [false, Numeric] wait
|
|
133
|
+
# Maximum time to wait for the action to succeed. Defaults to {Capybara.configure default_max_wait_time}.
|
|
127
134
|
# @return [Capybara::Node::Element] The element
|
|
128
135
|
def select_option(wait: nil)
|
|
129
136
|
synchronize(wait) { base.select_option }
|
|
@@ -132,8 +139,9 @@ module Capybara
|
|
|
132
139
|
|
|
133
140
|
##
|
|
134
141
|
#
|
|
135
|
-
# Unselect this node if is an option element inside a multiple select tag
|
|
142
|
+
# Unselect this node if it is an option element inside a multiple select tag.
|
|
136
143
|
#
|
|
144
|
+
# @macro action_waiting_behavior
|
|
137
145
|
# @return [Capybara::Node::Element] The element
|
|
138
146
|
def unselect_option(wait: nil)
|
|
139
147
|
synchronize(wait) { base.unselect_option }
|
|
@@ -142,11 +150,12 @@ module Capybara
|
|
|
142
150
|
|
|
143
151
|
##
|
|
144
152
|
#
|
|
145
|
-
# Click the Element
|
|
153
|
+
# Click the Element.
|
|
146
154
|
#
|
|
155
|
+
# @macro action_waiting_behavior
|
|
147
156
|
# @!macro click_modifiers
|
|
148
|
-
# Both x: and y: must be specified if an offset is wanted, if not specified the click will occur at the middle of the element
|
|
149
|
-
# @overload $0(*modifier_keys, **offset)
|
|
157
|
+
# Both x: and y: must be specified if an offset is wanted, if not specified the click will occur at the middle of the element.
|
|
158
|
+
# @overload $0(*modifier_keys, wait: nil, **offset)
|
|
150
159
|
# @param *modifier_keys [:alt, :control, :meta, :shift] ([]) Keys to be held down when clicking
|
|
151
160
|
# @option offset [Integer] x X coordinate to offset the click location from the top left corner of the element
|
|
152
161
|
# @option offset [Integer] y Y coordinate to offset the click location from the top left corner of the element
|
|
@@ -160,8 +169,9 @@ module Capybara
|
|
|
160
169
|
|
|
161
170
|
##
|
|
162
171
|
#
|
|
163
|
-
# Right Click the Element
|
|
172
|
+
# Right Click the Element.
|
|
164
173
|
#
|
|
174
|
+
# @macro action_waiting_behavior
|
|
165
175
|
# @macro click_modifiers
|
|
166
176
|
# @return [Capybara::Node::Element] The element
|
|
167
177
|
def right_click(*keys, wait: nil, **offset)
|
|
@@ -173,8 +183,9 @@ module Capybara
|
|
|
173
183
|
|
|
174
184
|
##
|
|
175
185
|
#
|
|
176
|
-
# Double Click the Element
|
|
186
|
+
# Double Click the Element.
|
|
177
187
|
#
|
|
188
|
+
# @macro action_waiting_behavior
|
|
178
189
|
# @macro click_modifiers
|
|
179
190
|
# @return [Capybara::Node::Element] The element
|
|
180
191
|
def double_click(*keys, wait: nil, **offset)
|
|
@@ -186,7 +197,7 @@ module Capybara
|
|
|
186
197
|
|
|
187
198
|
##
|
|
188
199
|
#
|
|
189
|
-
# Send Keystrokes to the Element
|
|
200
|
+
# Send Keystrokes to the Element.
|
|
190
201
|
#
|
|
191
202
|
# @overload send_keys(keys, ...)
|
|
192
203
|
# @param keys [String, Symbol, Array<String,Symbol>]
|
|
@@ -194,65 +205,65 @@ module Capybara
|
|
|
194
205
|
# Examples:
|
|
195
206
|
#
|
|
196
207
|
# element.send_keys "foo" #=> value: 'foo'
|
|
197
|
-
# element.send_keys "tet", :left, "s"
|
|
208
|
+
# element.send_keys "tet", :left, "s" #=> value: 'test'
|
|
198
209
|
# element.send_keys [:control, 'a'], :space #=> value: ' ' - assuming ctrl-a selects all contents
|
|
199
210
|
#
|
|
200
|
-
# Symbols supported for keys
|
|
201
|
-
# :cancel
|
|
202
|
-
# :help
|
|
203
|
-
# :backspace
|
|
204
|
-
# :tab
|
|
205
|
-
# :clear
|
|
206
|
-
# :return
|
|
207
|
-
# :enter
|
|
208
|
-
# :shift
|
|
209
|
-
# :control
|
|
210
|
-
# :alt
|
|
211
|
-
# :pause
|
|
212
|
-
# :escape
|
|
213
|
-
# :space
|
|
214
|
-
# :page_up
|
|
215
|
-
# :page_down
|
|
216
|
-
# :end
|
|
217
|
-
# :home
|
|
218
|
-
# :left
|
|
219
|
-
# :up
|
|
220
|
-
# :right
|
|
221
|
-
# :down
|
|
222
|
-
# :insert
|
|
223
|
-
# :delete
|
|
224
|
-
# :semicolon
|
|
225
|
-
# :equals
|
|
226
|
-
# :numpad0
|
|
227
|
-
# :numpad1
|
|
228
|
-
# :numpad2
|
|
229
|
-
# :numpad3
|
|
230
|
-
# :numpad4
|
|
231
|
-
# :numpad5
|
|
232
|
-
# :numpad6
|
|
233
|
-
# :numpad7
|
|
234
|
-
# :numpad8
|
|
235
|
-
# :numpad9
|
|
236
|
-
# :multiply - numeric keypad *
|
|
237
|
-
# :add - numeric keypad +
|
|
238
|
-
# :separator - numeric keypad 'separator' key ??
|
|
239
|
-
# :subtract - numeric keypad -
|
|
240
|
-
# :decimal - numeric keypad .
|
|
241
|
-
# :divide - numeric keypad /
|
|
242
|
-
# :f1
|
|
243
|
-
# :f2
|
|
244
|
-
# :f3
|
|
245
|
-
# :f4
|
|
246
|
-
# :f5
|
|
247
|
-
# :f6
|
|
248
|
-
# :f7
|
|
249
|
-
# :f8
|
|
250
|
-
# :f9
|
|
251
|
-
# :f10
|
|
252
|
-
# :f11
|
|
253
|
-
# :f12
|
|
254
|
-
# :meta
|
|
255
|
-
# :command - alias of :meta
|
|
211
|
+
# Symbols supported for keys:
|
|
212
|
+
# * :cancel
|
|
213
|
+
# * :help
|
|
214
|
+
# * :backspace
|
|
215
|
+
# * :tab
|
|
216
|
+
# * :clear
|
|
217
|
+
# * :return
|
|
218
|
+
# * :enter
|
|
219
|
+
# * :shift
|
|
220
|
+
# * :control
|
|
221
|
+
# * :alt
|
|
222
|
+
# * :pause
|
|
223
|
+
# * :escape
|
|
224
|
+
# * :space
|
|
225
|
+
# * :page_up
|
|
226
|
+
# * :page_down
|
|
227
|
+
# * :end
|
|
228
|
+
# * :home
|
|
229
|
+
# * :left
|
|
230
|
+
# * :up
|
|
231
|
+
# * :right
|
|
232
|
+
# * :down
|
|
233
|
+
# * :insert
|
|
234
|
+
# * :delete
|
|
235
|
+
# * :semicolon
|
|
236
|
+
# * :equals
|
|
237
|
+
# * :numpad0
|
|
238
|
+
# * :numpad1
|
|
239
|
+
# * :numpad2
|
|
240
|
+
# * :numpad3
|
|
241
|
+
# * :numpad4
|
|
242
|
+
# * :numpad5
|
|
243
|
+
# * :numpad6
|
|
244
|
+
# * :numpad7
|
|
245
|
+
# * :numpad8
|
|
246
|
+
# * :numpad9
|
|
247
|
+
# * :multiply - numeric keypad *
|
|
248
|
+
# * :add - numeric keypad +
|
|
249
|
+
# * :separator - numeric keypad 'separator' key ??
|
|
250
|
+
# * :subtract - numeric keypad -
|
|
251
|
+
# * :decimal - numeric keypad .
|
|
252
|
+
# * :divide - numeric keypad /
|
|
253
|
+
# * :f1
|
|
254
|
+
# * :f2
|
|
255
|
+
# * :f3
|
|
256
|
+
# * :f4
|
|
257
|
+
# * :f5
|
|
258
|
+
# * :f6
|
|
259
|
+
# * :f7
|
|
260
|
+
# * :f8
|
|
261
|
+
# * :f9
|
|
262
|
+
# * :f10
|
|
263
|
+
# * :f11
|
|
264
|
+
# * :f12
|
|
265
|
+
# * :meta
|
|
266
|
+
# * :command - alias of :meta
|
|
256
267
|
#
|
|
257
268
|
# @return [Capybara::Node::Element] The element
|
|
258
269
|
def send_keys(*args)
|
|
@@ -262,7 +273,7 @@ module Capybara
|
|
|
262
273
|
|
|
263
274
|
##
|
|
264
275
|
#
|
|
265
|
-
# Hover on the Element
|
|
276
|
+
# Hover on the Element.
|
|
266
277
|
#
|
|
267
278
|
# @return [Capybara::Node::Element] The element
|
|
268
279
|
def hover
|
|
@@ -276,7 +287,7 @@ module Capybara
|
|
|
276
287
|
#
|
|
277
288
|
def tag_name
|
|
278
289
|
# Element type is immutable so cache it
|
|
279
|
-
@tag_name ||= synchronize { base.tag_name }
|
|
290
|
+
@tag_name ||= initial_cache[:tag_name] || synchronize { base.tag_name }
|
|
280
291
|
end
|
|
281
292
|
|
|
282
293
|
##
|
|
@@ -353,7 +364,7 @@ module Capybara
|
|
|
353
364
|
|
|
354
365
|
##
|
|
355
366
|
#
|
|
356
|
-
# An XPath expression describing where on the page the element can be found
|
|
367
|
+
# An XPath expression describing where on the page the element can be found.
|
|
357
368
|
#
|
|
358
369
|
# @return [String] An XPath expression
|
|
359
370
|
#
|
|
@@ -394,17 +405,41 @@ module Capybara
|
|
|
394
405
|
|
|
395
406
|
##
|
|
396
407
|
#
|
|
397
|
-
#
|
|
408
|
+
# Drop items on the current element.
|
|
409
|
+
#
|
|
410
|
+
# target = page.find('#foo')
|
|
411
|
+
# target.drop('/some/path/file.csv')
|
|
412
|
+
#
|
|
413
|
+
# @overload drop(path, ...)
|
|
414
|
+
# @param [String, #to_path] path Location of the file to drop on the element
|
|
415
|
+
#
|
|
416
|
+
# @overload drop(strings, ...)
|
|
417
|
+
# @param [Hash] strings A hash of type to data to be dropped - `{ "text/url" => "https://www.google.com" }`
|
|
418
|
+
#
|
|
419
|
+
# @return [Capybara::Node::Element] The element
|
|
420
|
+
def drop(*args)
|
|
421
|
+
options = args.map do |arg|
|
|
422
|
+
return arg.to_path if arg.respond_to?(:to_path)
|
|
423
|
+
|
|
424
|
+
arg
|
|
425
|
+
end
|
|
426
|
+
synchronize { base.drop(*options) }
|
|
427
|
+
self
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
##
|
|
431
|
+
#
|
|
432
|
+
# Scroll the page or element.
|
|
398
433
|
#
|
|
399
|
-
# Scroll the page or element to its top, bottom or middle
|
|
400
434
|
# @overload scroll_to(position, offset: [0,0])
|
|
435
|
+
# Scroll the page or element to its top, bottom or middle.
|
|
401
436
|
# @param [:top, :bottom, :center, :current] position
|
|
402
|
-
# @param
|
|
437
|
+
# @param [[Integer, Integer]] offset
|
|
403
438
|
#
|
|
404
|
-
# Scroll the page or current element until the given element is aligned at the top, bottom, or center of it
|
|
405
439
|
# @overload scroll_to(element, align: :top)
|
|
440
|
+
# Scroll the page or current element until the given element is aligned at the top, bottom, or center of it.
|
|
406
441
|
# @param [Capybara::Node::Element] element The element to be scrolled into view
|
|
407
|
-
# @param [:top, :bottom, :center]
|
|
442
|
+
# @param [:top, :bottom, :center] align Where to align the element being scrolled into view with relation to the current page/element if possible
|
|
408
443
|
#
|
|
409
444
|
# @overload scroll_to(x,y)
|
|
410
445
|
# @param [Integer] x Horizontal scroll offset
|
|
@@ -427,11 +462,11 @@ module Capybara
|
|
|
427
462
|
##
|
|
428
463
|
#
|
|
429
464
|
# Execute the given JS in the context of the element not returning a result. This is useful for scripts that return
|
|
430
|
-
# complex objects, such as jQuery statements.
|
|
431
|
-
#
|
|
465
|
+
# complex objects, such as jQuery statements. {#execute_script} should be used over
|
|
466
|
+
# {#evaluate_script} whenever a result is not expected or needed. `this` in the script will refer to the element this is called on.
|
|
432
467
|
#
|
|
433
468
|
# @param [String] script A string of JavaScript to execute
|
|
434
|
-
# @param args Optional arguments that will be passed to the script.
|
|
469
|
+
# @param args Optional arguments that will be passed to the script. Driver support for this is optional and types of objects supported may differ between drivers
|
|
435
470
|
#
|
|
436
471
|
def execute_script(script, *args)
|
|
437
472
|
session.execute_script(<<~JS, self, *args)
|
|
@@ -444,7 +479,7 @@ module Capybara
|
|
|
444
479
|
##
|
|
445
480
|
#
|
|
446
481
|
# Evaluate the given JS in the context of the element and return the result. Be careful when using this with
|
|
447
|
-
# scripts that return complex objects, such as jQuery statements.
|
|
482
|
+
# scripts that return complex objects, such as jQuery statements. {#execute_script} might
|
|
448
483
|
# be a better alternative. `this` in the script will refer to the element this is called on.
|
|
449
484
|
#
|
|
450
485
|
# @param [String] script A string of JavaScript to evaluate
|
|
@@ -462,7 +497,7 @@ module Capybara
|
|
|
462
497
|
#
|
|
463
498
|
# Evaluate the given JavaScript in the context of the element and obtain the result from a
|
|
464
499
|
# callback function which will be passed as the last argument to the script. `this` in the
|
|
465
|
-
# script will refer to the element this is called on
|
|
500
|
+
# script will refer to the element this is called on.
|
|
466
501
|
#
|
|
467
502
|
# @param [String] script A string of JavaScript to evaluate
|
|
468
503
|
# @return [Object] The result of the evaluated JavaScript (may be driver specific)
|
|
@@ -475,6 +510,7 @@ module Capybara
|
|
|
475
510
|
JS
|
|
476
511
|
end
|
|
477
512
|
|
|
513
|
+
# @api private
|
|
478
514
|
def reload
|
|
479
515
|
if @allow_reload
|
|
480
516
|
begin
|
|
@@ -487,6 +523,11 @@ module Capybara
|
|
|
487
523
|
self
|
|
488
524
|
end
|
|
489
525
|
|
|
526
|
+
##
|
|
527
|
+
#
|
|
528
|
+
# A human-readable representation of the element.
|
|
529
|
+
#
|
|
530
|
+
# @return [String] A string representation
|
|
490
531
|
def inspect
|
|
491
532
|
%(#<Capybara::Node::Element tag="#{base.tag_name}" path="#{base.path}">)
|
|
492
533
|
rescue NotSupportedByDriverError
|
|
@@ -8,19 +8,14 @@ module Capybara
|
|
|
8
8
|
# Find an {Capybara::Node::Element} based on the given arguments. +find+ will raise an error if the element
|
|
9
9
|
# is not found.
|
|
10
10
|
#
|
|
11
|
-
# @!macro waiting_behavior
|
|
12
|
-
# If the driver is capable of executing JavaScript, this method will wait for a set amount of time
|
|
13
|
-
# and continuously retry finding the element until either the element is found or the time
|
|
14
|
-
# expires. The length of time +find+ will wait is controlled through {Capybara.default_max_wait_time}
|
|
15
|
-
# and defaults to 2 seconds.
|
|
16
|
-
# @option options [false, true, Numeric] wait (Capybara.default_max_wait_time) Maximum time to wait for matching element to appear.
|
|
17
|
-
#
|
|
18
11
|
# page.find('#foo').find('.bar')
|
|
19
12
|
# page.find(:xpath, './/div[contains(., "bar")]')
|
|
20
13
|
# page.find('li', text: 'Quox').click_link('Delete')
|
|
21
14
|
#
|
|
22
15
|
# @param (see Capybara::Node::Finders#all)
|
|
23
16
|
#
|
|
17
|
+
# @macro waiting_behavior
|
|
18
|
+
#
|
|
24
19
|
# @!macro system_filters
|
|
25
20
|
# @option options [String, Regexp] text Only find elements which contain this text or match this regexp
|
|
26
21
|
# @option options [String, Boolean] exact_text (Capybara.exact_text) When String the elements contained text must match exactly, when Boolean controls whether the :text option must match exactly
|
data/lib/capybara/node/simple.rb
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
Capybara.add_selector(:css, locator_type: [String, Symbol], raw_locator: true) do
|
|
4
|
-
css
|
|
4
|
+
css do |css|
|
|
5
|
+
warn "DEPRECATED: Passing a symbol (#{css.inspect}) as the CSS locator is deprecated - please pass a string instead." if css.is_a? Symbol
|
|
6
|
+
css
|
|
7
|
+
end
|
|
5
8
|
end
|
|
@@ -22,6 +22,69 @@ class Capybara::Selenium::Node
|
|
|
22
22
|
native.property('draggable')
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
def html5_drop(*args)
|
|
26
|
+
if args[0].is_a? String
|
|
27
|
+
input = driver.evaluate_script ATTACH_FILE
|
|
28
|
+
input.set_file(args)
|
|
29
|
+
driver.execute_script DROP_FILE, self, input
|
|
30
|
+
else
|
|
31
|
+
items = args.each_with_object([]) do |arg, arr|
|
|
32
|
+
arg.each_with_object(arr) do |(type, data), arr_|
|
|
33
|
+
arr_ << { type: type, data: data }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
driver.execute_script DROP_STRING, items, self
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
DROP_STRING = <<~JS
|
|
41
|
+
var strings = arguments[0],
|
|
42
|
+
el = arguments[1],
|
|
43
|
+
dt = new DataTransfer(),
|
|
44
|
+
opts = { cancelable: true, bubbles: true, dataTransfer: dt };
|
|
45
|
+
for (var i=0; i < strings.length; i++){
|
|
46
|
+
if (dt.items) {
|
|
47
|
+
dt.items.add(strings[i]['data'], strings[i]['type']);
|
|
48
|
+
} else {
|
|
49
|
+
dt.setData(strings[i]['type'], strings[i]['data']);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
var dropEvent = new DragEvent('drop', opts);
|
|
53
|
+
el.dispatchEvent(dropEvent);
|
|
54
|
+
JS
|
|
55
|
+
|
|
56
|
+
DROP_FILE = <<~JS
|
|
57
|
+
var el = arguments[0],
|
|
58
|
+
input = arguments[1],
|
|
59
|
+
files = input.files,
|
|
60
|
+
dt = new DataTransfer(),
|
|
61
|
+
opts = { cancelable: true, bubbles: true, dataTransfer: dt };
|
|
62
|
+
input.parentElement.removeChild(input);
|
|
63
|
+
if (dt.items){
|
|
64
|
+
for (var i=0; i<files.length; i++){
|
|
65
|
+
dt.items.add(files[i]);
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
Object.defineProperty(dt, "files", {
|
|
69
|
+
value: files,
|
|
70
|
+
writable: false
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
var dropEvent = new DragEvent('drop', opts);
|
|
74
|
+
el.dispatchEvent(dropEvent);
|
|
75
|
+
JS
|
|
76
|
+
|
|
77
|
+
ATTACH_FILE = <<~JS
|
|
78
|
+
(function(){
|
|
79
|
+
var input = document.createElement('INPUT');
|
|
80
|
+
input.type = "file";
|
|
81
|
+
input.id = "_capybara_drop_file";
|
|
82
|
+
input.multiple = true;
|
|
83
|
+
document.body.appendChild(input);
|
|
84
|
+
return input;
|
|
85
|
+
})()
|
|
86
|
+
JS
|
|
87
|
+
|
|
25
88
|
MOUSEDOWN_TRACKER = <<~JS
|
|
26
89
|
document.addEventListener('mousedown', ev => {
|
|
27
90
|
window.capybara_mousedown_prevented = ev.defaultPrevented;
|
|
@@ -137,6 +137,10 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
|
137
137
|
element.scroll_if_needed { browser_action.move_to(element.native).release.perform }
|
|
138
138
|
end
|
|
139
139
|
|
|
140
|
+
def drop(*_)
|
|
141
|
+
raise NotImplementedError, 'Out of browser drop emulation is not implemented for the current browser'
|
|
142
|
+
end
|
|
143
|
+
|
|
140
144
|
def tag_name
|
|
141
145
|
@tag_name ||= native.tag_name.downcase
|
|
142
146
|
end
|
|
@@ -52,6 +52,10 @@ class Capybara::Selenium::FirefoxNode < Capybara::Selenium::Node
|
|
|
52
52
|
html5_drag_to(element)
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
+
def drop(*args)
|
|
56
|
+
html5_drop(*args)
|
|
57
|
+
end
|
|
58
|
+
|
|
55
59
|
def hover
|
|
56
60
|
return super unless browser_version >= 65.0
|
|
57
61
|
|
|
@@ -117,19 +121,6 @@ private
|
|
|
117
121
|
driver.browser.capabilities[:browser_version].to_f
|
|
118
122
|
end
|
|
119
123
|
|
|
120
|
-
DISABLED_BY_FIELDSET_XPATH = XPath.generate do |x|
|
|
121
|
-
x.parent(:fieldset)[
|
|
122
|
-
x.attr(:disabled)
|
|
123
|
-
] + x.ancestor[
|
|
124
|
-
~x.self(:legend) |
|
|
125
|
-
x.preceding_sibling(:legend)
|
|
126
|
-
][
|
|
127
|
-
x.parent(:fieldset)[
|
|
128
|
-
x.attr(:disabled)
|
|
129
|
-
]
|
|
130
|
-
]
|
|
131
|
-
end.to_s.freeze
|
|
132
|
-
|
|
133
124
|
class ModifierKeysStack
|
|
134
125
|
def initialize
|
|
135
126
|
@stack = []
|
|
@@ -4,8 +4,19 @@ require 'capybara/selenium/extensions/html5_drag'
|
|
|
4
4
|
|
|
5
5
|
class Capybara::Selenium::IENode < Capybara::Selenium::Node
|
|
6
6
|
def disabled?
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
|
|
7
|
+
# super
|
|
8
|
+
# optimize to one script call
|
|
9
|
+
driver.evaluate_script <<~JS.delete("\n"), self
|
|
10
|
+
arguments[0].msMatchesSelector('
|
|
11
|
+
:disabled,
|
|
12
|
+
select:disabled *,
|
|
13
|
+
optgroup:disabled *,
|
|
14
|
+
fieldset[disabled],
|
|
15
|
+
fieldset[disabled] > *:not(legend),
|
|
16
|
+
fieldset[disabled] > *:not(legend) *,
|
|
17
|
+
fieldset[disabled] > legend:nth-of-type(n+2),
|
|
18
|
+
fieldset[disabled] > legend:nth-of-type(n+2) *
|
|
19
|
+
')
|
|
20
|
+
JS
|
|
10
21
|
end
|
|
11
22
|
end
|
|
@@ -91,19 +91,6 @@ private
|
|
|
91
91
|
driver.browser.send(:bridge)
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
-
DISABLED_BY_FIELDSET_XPATH = XPath.generate do |x|
|
|
95
|
-
x.parent(:fieldset)[
|
|
96
|
-
x.attr(:disabled)
|
|
97
|
-
] + x.ancestor[
|
|
98
|
-
~x.self(:legend) |
|
|
99
|
-
x.preceding_sibling(:legend)
|
|
100
|
-
][
|
|
101
|
-
x.parent(:fieldset)[
|
|
102
|
-
x.attr(:disabled)
|
|
103
|
-
]
|
|
104
|
-
]
|
|
105
|
-
end.to_s.freeze
|
|
106
|
-
|
|
107
94
|
def _send_keys(keys, actions = browser_action, down_keys = ModifierKeysStack.new)
|
|
108
95
|
case keys
|
|
109
96
|
when *MODIFIER_KEYS
|
|
@@ -18,7 +18,33 @@ $(function() {
|
|
|
18
18
|
});
|
|
19
19
|
$('#drop_html5, #drop_html5_scroll').on('drop', function(ev){
|
|
20
20
|
ev.preventDefault();
|
|
21
|
-
|
|
21
|
+
var oev = ev.originalEvent;
|
|
22
|
+
if (oev.dataTransfer.items) {
|
|
23
|
+
for (var i = 0; i < oev.dataTransfer.items.length; i++){
|
|
24
|
+
var item = oev.dataTransfer.items[i];
|
|
25
|
+
if (item.kind === 'file'){
|
|
26
|
+
var file = item.getAsFile();
|
|
27
|
+
$(this).append('HTML5 Dropped file: ' + file.name);
|
|
28
|
+
} else {
|
|
29
|
+
var _this = this;
|
|
30
|
+
var callback = (function(type){
|
|
31
|
+
return function(s){
|
|
32
|
+
$(_this).append('HTML5 Dropped string: ' + type + ' ' + s)
|
|
33
|
+
}
|
|
34
|
+
})(item.type);
|
|
35
|
+
item.getAsString(callback);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
$(this).html('HTML5 Dropped ' + oev.dataTransfer.getData("text"));
|
|
40
|
+
for (var i = 0; i < oev.dataTransfer.files.length; i++) {
|
|
41
|
+
$(this).append('HTML5 Dropped file: ' + oev.dataTransfer.files[i].name);
|
|
42
|
+
}
|
|
43
|
+
for (var i = 0; i < oev.dataTransfer.types.length; i++) {
|
|
44
|
+
var type = oev.dataTransfer.types[i];
|
|
45
|
+
$(this).append('HTML5 Dropped string: ' + type + ' ' + oev.dataTransfer.getData(type));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
22
48
|
});
|
|
23
49
|
$('#clickable').click(function(e) {
|
|
24
50
|
var link = $(this);
|
|
@@ -180,7 +206,7 @@ $(function() {
|
|
|
180
206
|
sessionStorage.setItem('session', 'session_value');
|
|
181
207
|
localStorage.setItem('local', 'local value');
|
|
182
208
|
});
|
|
183
|
-
$('#multiple-file').change(function(e){
|
|
184
|
-
$('body').append($('<p class="file_change"
|
|
209
|
+
$('#multiple-file, #hidden_file').change(function(e){
|
|
210
|
+
$('body').append($('<p class="file_change">File input changed</p>'));
|
|
185
211
|
})
|
|
186
212
|
});
|
|
@@ -180,6 +180,12 @@ Capybara::SpecHelper.spec '#attach_file' do
|
|
|
180
180
|
@session.attach_file('hidden_file', with_os_path_separators(__FILE__), make_visible: true)
|
|
181
181
|
expect(@session.evaluate_script('arguments[0].style.display', @session.find(:css, '#hidden_file', visible: :all))).to eq 'none'
|
|
182
182
|
end
|
|
183
|
+
|
|
184
|
+
it 'should fire change' do
|
|
185
|
+
@session.visit('/with_js')
|
|
186
|
+
@session.attach_file('hidden_file', with_os_path_separators(__FILE__), make_visible: true)
|
|
187
|
+
expect(@session).to have_css('.file_change')
|
|
188
|
+
end
|
|
183
189
|
end
|
|
184
190
|
|
|
185
191
|
context 'with a block', requires: %i[js] do
|
|
@@ -198,11 +204,13 @@ Capybara::SpecHelper.spec '#attach_file' do
|
|
|
198
204
|
@session.click_button('awesome')
|
|
199
205
|
expect(extract_results(@session)['hidden_image']).to end_with(File.basename(__FILE__))
|
|
200
206
|
end
|
|
201
|
-
end
|
|
202
207
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
208
|
+
it 'should fire change' do
|
|
209
|
+
@session.visit('/with_js')
|
|
210
|
+
@session.attach_file(with_os_path_separators(__FILE__)) do
|
|
211
|
+
@session.find(:label, 'Label for hidden file input').click
|
|
212
|
+
end
|
|
213
|
+
expect(@session).to have_css('.file_change')
|
|
214
|
+
end
|
|
207
215
|
end
|
|
208
216
|
end
|
|
@@ -10,10 +10,11 @@ Capybara::SpecHelper.spec '#has_css?' do
|
|
|
10
10
|
expect(@session).to have_css('p a#foo')
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
it 'should
|
|
13
|
+
it 'should warn when passed a symbol' do
|
|
14
14
|
# This was never a specifically accepted format but it has worked for a
|
|
15
|
-
# lot of versions.
|
|
16
|
-
# TODO:
|
|
15
|
+
# lot of versions.
|
|
16
|
+
# TODO: Remove in 4.0
|
|
17
|
+
expect_any_instance_of(Kernel).to receive(:warn) # rubocop:disable RSpec/AnyInstance
|
|
17
18
|
expect(@session).to have_css(:p)
|
|
18
19
|
end
|
|
19
20
|
|