capybara 3.6.0 → 3.7.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 +16 -0
- data/README.md +5 -1
- data/lib/capybara.rb +2 -0
- data/lib/capybara/minitest/spec.rb +1 -1
- data/lib/capybara/node/actions.rb +34 -25
- data/lib/capybara/node/base.rb +15 -17
- data/lib/capybara/node/document_matchers.rb +1 -3
- data/lib/capybara/node/element.rb +11 -12
- data/lib/capybara/node/finders.rb +2 -1
- data/lib/capybara/node/simple.rb +13 -6
- data/lib/capybara/queries/base_query.rb +4 -4
- data/lib/capybara/queries/selector_query.rb +119 -94
- data/lib/capybara/queries/text_query.rb +2 -1
- data/lib/capybara/rack_test/form.rb +4 -4
- data/lib/capybara/rack_test/node.rb +5 -5
- data/lib/capybara/result.rb +23 -32
- data/lib/capybara/rspec/compound.rb +1 -1
- data/lib/capybara/rspec/matchers.rb +63 -61
- data/lib/capybara/selector.rb +28 -10
- data/lib/capybara/selector/css.rb +17 -17
- data/lib/capybara/selector/filter_set.rb +9 -9
- data/lib/capybara/selector/selector.rb +3 -4
- data/lib/capybara/selenium/driver.rb +73 -95
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +4 -4
- data/lib/capybara/selenium/driver_specializations/marionette_driver.rb +9 -0
- data/lib/capybara/selenium/node.rb +127 -67
- data/lib/capybara/selenium/nodes/chrome_node.rb +3 -3
- data/lib/capybara/selenium/nodes/marionette_node.rb +14 -8
- data/lib/capybara/server.rb +2 -2
- data/lib/capybara/server/animation_disabler.rb +17 -3
- data/lib/capybara/server/middleware.rb +8 -4
- data/lib/capybara/session.rb +43 -37
- data/lib/capybara/session/config.rb +8 -6
- data/lib/capybara/spec/session/assert_text_spec.rb +14 -0
- data/lib/capybara/spec/session/attach_file_spec.rb +7 -0
- data/lib/capybara/spec/session/check_spec.rb +21 -0
- data/lib/capybara/spec/session/choose_spec.rb +15 -1
- data/lib/capybara/spec/session/fill_in_spec.rb +7 -0
- data/lib/capybara/spec/session/find_spec.rb +2 -1
- data/lib/capybara/spec/session/has_selector_spec.rb +18 -0
- data/lib/capybara/spec/session/has_text_spec.rb +14 -0
- data/lib/capybara/spec/session/node_spec.rb +2 -1
- data/lib/capybara/spec/session/reset_session_spec.rb +4 -4
- data/lib/capybara/spec/session/text_spec.rb +2 -1
- data/lib/capybara/spec/session/title_spec.rb +2 -1
- data/lib/capybara/spec/session/uncheck_spec.rb +8 -0
- data/lib/capybara/spec/session/within_spec.rb +2 -1
- data/lib/capybara/spec/spec_helper.rb +1 -32
- data/lib/capybara/spec/views/with_js.erb +3 -4
- data/lib/capybara/version.rb +1 -1
- data/spec/minitest_spec.rb +4 -0
- data/spec/minitest_spec_spec.rb +4 -0
- data/spec/rack_test_spec.rb +4 -4
- data/spec/rspec/shared_spec_matchers.rb +4 -2
- data/spec/selector_spec.rb +15 -1
- data/spec/selenium_spec_chrome.rb +1 -6
- data/spec/selenium_spec_chrome_remote.rb +1 -1
- data/spec/selenium_spec_firefox_remote.rb +2 -5
- data/spec/selenium_spec_ie.rb +41 -4
- data/spec/selenium_spec_marionette.rb +1 -25
- data/spec/shared_selenium_session.rb +74 -16
- data/spec/spec_helper.rb +41 -0
- metadata +2 -2
@@ -7,8 +7,8 @@ module Capybara::Selenium::Driver::ChromeDriver
|
|
7
7
|
within_given_window(handle) do
|
8
8
|
begin
|
9
9
|
super
|
10
|
-
rescue NoMethodError =>
|
11
|
-
raise unless
|
10
|
+
rescue NoMethodError => err
|
11
|
+
raise unless err.message =~ /full_screen_window/
|
12
12
|
bridge = browser.send(:bridge)
|
13
13
|
result = bridge.http.call(:post, "session/#{bridge.session_id}/window/fullscreen", {})
|
14
14
|
result['value']
|
@@ -18,8 +18,8 @@ module Capybara::Selenium::Driver::ChromeDriver
|
|
18
18
|
|
19
19
|
def resize_window_to(handle, width, height)
|
20
20
|
super
|
21
|
-
rescue Selenium::WebDriver::Error::UnknownError =>
|
22
|
-
raise unless
|
21
|
+
rescue Selenium::WebDriver::Error::UnknownError => err
|
22
|
+
raise unless err.message =~ /failed to change window state/
|
23
23
|
# Chromedriver doesn't wait long enough for state to change when coming out of fullscreen
|
24
24
|
# and raises unnecessary error. Wait a bit and try again.
|
25
25
|
sleep 0.5
|
@@ -23,6 +23,15 @@ module Capybara::Selenium::Driver::MarionetteDriver
|
|
23
23
|
super
|
24
24
|
end
|
25
25
|
|
26
|
+
def refresh
|
27
|
+
# Accept any "will repost content" confirmation that occurs
|
28
|
+
accept_modal :confirm, wait: 0.1 do
|
29
|
+
super
|
30
|
+
end
|
31
|
+
rescue Capybara::ModalNotFound # rubocop:disable Lint/HandleExceptions
|
32
|
+
# No modal was opened - page has refreshed - ignore
|
33
|
+
end
|
34
|
+
|
26
35
|
private
|
27
36
|
|
28
37
|
def build_node(native_node)
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# Selenium specific implementation of the Capybara::Driver::Node API
|
3
4
|
class Capybara::Selenium::Node < Capybara::Driver::Node
|
4
5
|
def visible_text
|
5
6
|
native.text
|
@@ -22,7 +23,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
22
23
|
|
23
24
|
def value
|
24
25
|
if tag_name == 'select' && multiple?
|
25
|
-
native.find_elements(:css, 'option:checked').map { |
|
26
|
+
native.find_elements(:css, 'option:checked').map { |el| el[:value] || el.text }
|
26
27
|
else
|
27
28
|
native[:value]
|
28
29
|
end
|
@@ -73,46 +74,38 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
73
74
|
end
|
74
75
|
|
75
76
|
def select_option
|
76
|
-
|
77
|
+
click unless selected? || disabled?
|
77
78
|
end
|
78
79
|
|
79
80
|
def unselect_option
|
80
81
|
raise Capybara::UnselectNotAllowed, 'Cannot unselect option from single select box.' unless select_node.multiple?
|
81
|
-
|
82
|
+
click if selected?
|
82
83
|
end
|
83
84
|
|
84
85
|
def click(keys = [], **options)
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
rescue StandardError => e
|
95
|
-
if e.is_a?(::Selenium::WebDriver::Error::ElementClickInterceptedError) ||
|
96
|
-
e.message =~ /Other element would receive the click/
|
86
|
+
click_options = ClickOptions.new(keys, options)
|
87
|
+
return native.click if click_options.empty?
|
88
|
+
click_with_options(click_options)
|
89
|
+
rescue StandardError => err
|
90
|
+
if err.is_a?(::Selenium::WebDriver::Error::ElementClickInterceptedError) ||
|
91
|
+
err.message =~ /Other element would receive the click/
|
97
92
|
scroll_to_center
|
98
93
|
end
|
99
94
|
|
100
|
-
raise
|
95
|
+
raise err
|
101
96
|
end
|
102
97
|
|
103
98
|
def right_click(keys = [], **options)
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
end
|
99
|
+
click_options = ClickOptions.new(keys, options)
|
100
|
+
click_with_options(click_options) do |action|
|
101
|
+
click_options.coords? ? action.context_click : action.context_click(native)
|
108
102
|
end
|
109
103
|
end
|
110
104
|
|
111
105
|
def double_click(keys = [], **options)
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
end
|
106
|
+
click_options = ClickOptions.new(keys, options)
|
107
|
+
click_with_options(click_options) do |action|
|
108
|
+
click_options.coords? ? action.double_click : action.double_click(native)
|
116
109
|
end
|
117
110
|
end
|
118
111
|
|
@@ -121,11 +114,14 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
121
114
|
end
|
122
115
|
|
123
116
|
def hover
|
124
|
-
scroll_if_needed {
|
117
|
+
scroll_if_needed { browser_action.move_to(native).perform }
|
125
118
|
end
|
126
119
|
|
127
120
|
def drag_to(element)
|
128
|
-
|
121
|
+
# Due to W3C spec compliance - The Actions API no longer scrolls to elements when necessary
|
122
|
+
# which means Seleniums `drag_and_drop` is now broken - do it manually
|
123
|
+
scroll_if_needed { browser_action.click_and_hold(native).perform }
|
124
|
+
element.scroll_if_needed { browser_action.move_to(element.native).release.perform }
|
129
125
|
end
|
130
126
|
|
131
127
|
def tag_name
|
@@ -149,11 +145,11 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
149
145
|
end
|
150
146
|
|
151
147
|
def find_xpath(locator)
|
152
|
-
native.find_elements(:xpath, locator).map { |
|
148
|
+
native.find_elements(:xpath, locator).map { |el| self.class.new(driver, el) }
|
153
149
|
end
|
154
150
|
|
155
151
|
def find_css(locator)
|
156
|
-
native.find_elements(:css, locator).map { |
|
152
|
+
native.find_elements(:css, locator).map { |el| self.class.new(driver, el) }
|
157
153
|
end
|
158
154
|
|
159
155
|
def ==(other)
|
@@ -190,12 +186,17 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
190
186
|
'/' + result.reverse.join('/')
|
191
187
|
end
|
192
188
|
|
193
|
-
|
189
|
+
protected
|
194
190
|
|
195
|
-
def
|
196
|
-
|
191
|
+
def scroll_if_needed
|
192
|
+
yield
|
193
|
+
rescue ::Selenium::WebDriver::Error::MoveTargetOutOfBoundsError
|
194
|
+
scroll_to_center
|
195
|
+
yield
|
197
196
|
end
|
198
197
|
|
198
|
+
private
|
199
|
+
|
199
200
|
def boolean_attr(val)
|
200
201
|
val && (val != 'false')
|
201
202
|
end
|
@@ -206,30 +207,34 @@ private
|
|
206
207
|
end
|
207
208
|
|
208
209
|
def set_text(value, clear: nil, **_unused)
|
209
|
-
|
210
|
+
value = value.to_s
|
211
|
+
if value.empty? && clear.nil?
|
210
212
|
native.clear
|
211
213
|
elsif clear == :backspace
|
212
214
|
# Clear field by sending the correct number of backspace keys.
|
213
215
|
backspaces = [:backspace] * self.value.to_s.length
|
214
|
-
send_keys(*([:end] + backspaces + [value
|
215
|
-
elsif clear == :none
|
216
|
-
send_keys(value.to_s)
|
216
|
+
send_keys(*([:end] + backspaces + [value]))
|
217
217
|
elsif clear.is_a? Array
|
218
|
-
send_keys(*clear, value
|
218
|
+
send_keys(*clear, value)
|
219
219
|
else
|
220
220
|
# Clear field by JavaScript assignment of the value property.
|
221
221
|
# Script can change a readonly element which user input cannot, so
|
222
222
|
# don't execute if readonly.
|
223
|
-
driver.execute_script "arguments[0].value = ''", self
|
224
|
-
send_keys(value
|
223
|
+
driver.execute_script "arguments[0].value = ''", self unless clear == :none
|
224
|
+
send_keys(value)
|
225
225
|
end
|
226
226
|
end
|
227
227
|
|
228
|
-
def
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
228
|
+
def click_with_options(click_options)
|
229
|
+
scroll_if_needed do
|
230
|
+
action_with_modifiers(click_options) do |action|
|
231
|
+
if block_given?
|
232
|
+
yield action
|
233
|
+
else
|
234
|
+
click_options.coords? ? action.click : action.click(native)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
233
238
|
end
|
234
239
|
|
235
240
|
def scroll_to_center
|
@@ -248,21 +253,24 @@ private
|
|
248
253
|
end
|
249
254
|
|
250
255
|
def set_date(value) # rubocop:disable Naming/AccessorMethodName
|
251
|
-
|
256
|
+
value = SettableValue.new(value)
|
257
|
+
return set_text(value) unless value.dateable?
|
252
258
|
# TODO: this would be better if locale can be detected and correct keystrokes sent
|
253
|
-
update_value_js(value.
|
259
|
+
update_value_js(value.to_date_str)
|
254
260
|
end
|
255
261
|
|
256
262
|
def set_time(value) # rubocop:disable Naming/AccessorMethodName
|
257
|
-
|
263
|
+
value = SettableValue.new(value)
|
264
|
+
return set_text(value) unless value.timeable?
|
258
265
|
# TODO: this would be better if locale can be detected and correct keystrokes sent
|
259
|
-
update_value_js(value.
|
266
|
+
update_value_js(value.to_time_str)
|
260
267
|
end
|
261
268
|
|
262
269
|
def set_datetime_local(value) # rubocop:disable Naming/AccessorMethodName
|
263
|
-
|
270
|
+
value = SettableValue.new(value)
|
271
|
+
return set_text(value) unless value.timeable?
|
264
272
|
# TODO: this would be better if locale can be detected and correct keystrokes sent
|
265
|
-
update_value_js(value.
|
273
|
+
update_value_js(value.to_datetime_str)
|
266
274
|
end
|
267
275
|
|
268
276
|
def update_value_js(value)
|
@@ -301,34 +309,33 @@ private
|
|
301
309
|
# if we use the faster direct send_keys. For now just send_keys to the element
|
302
310
|
# we've already focused.
|
303
311
|
# native.send_keys(value.to_s)
|
304
|
-
|
312
|
+
browser_action.send_keys(value.to_s).perform
|
305
313
|
end
|
306
314
|
|
307
|
-
def action_with_modifiers(
|
308
|
-
actions =
|
309
|
-
actions
|
310
|
-
modifiers_down(actions, keys)
|
315
|
+
def action_with_modifiers(click_options)
|
316
|
+
actions = browser_action.move_to(native, *click_options.coords)
|
317
|
+
modifiers_down(actions, click_options.keys)
|
311
318
|
yield actions
|
312
|
-
modifiers_up(actions, keys)
|
319
|
+
modifiers_up(actions, click_options.keys)
|
313
320
|
actions.perform
|
314
321
|
ensure
|
315
|
-
|
316
|
-
|
322
|
+
act = browser_action
|
323
|
+
act.release_actions if act.respond_to?(:release_actions)
|
317
324
|
end
|
318
325
|
|
319
326
|
def modifiers_down(actions, keys)
|
320
|
-
keys
|
321
|
-
key = case key
|
322
|
-
when :ctrl then :control
|
323
|
-
when :command, :cmd then :meta
|
324
|
-
else
|
325
|
-
key
|
326
|
-
end
|
327
|
-
actions.key_down(key)
|
328
|
-
end
|
327
|
+
each_key(keys) { |key| actions.key_down(key) }
|
329
328
|
end
|
330
329
|
|
331
330
|
def modifiers_up(actions, keys)
|
331
|
+
each_key(keys) { |key| actions.key_up(key) }
|
332
|
+
end
|
333
|
+
|
334
|
+
def browser_action
|
335
|
+
driver.browser.action
|
336
|
+
end
|
337
|
+
|
338
|
+
def each_key(keys)
|
332
339
|
keys.each do |key|
|
333
340
|
key = case key
|
334
341
|
when :ctrl then :control
|
@@ -336,7 +343,60 @@ private
|
|
336
343
|
else
|
337
344
|
key
|
338
345
|
end
|
339
|
-
|
346
|
+
yield key
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
# SettableValue encapsulates time/date field formatting
|
351
|
+
class SettableValue
|
352
|
+
attr_reader :value
|
353
|
+
|
354
|
+
def initialize(value)
|
355
|
+
@value = value
|
356
|
+
end
|
357
|
+
|
358
|
+
def dateable?
|
359
|
+
!value.is_a?(String) && value.respond_to?(:to_date)
|
360
|
+
end
|
361
|
+
|
362
|
+
def to_date_str
|
363
|
+
value.to_date.strftime('%Y-%m-%d')
|
364
|
+
end
|
365
|
+
|
366
|
+
def timeable?
|
367
|
+
!value.is_a?(String) && value.respond_to?(:to_time)
|
368
|
+
end
|
369
|
+
|
370
|
+
def to_time_str
|
371
|
+
value.to_time.strftime('%H:%M')
|
372
|
+
end
|
373
|
+
|
374
|
+
def to_datetime_str
|
375
|
+
value.to_time.strftime('%Y-%m-%dT%H:%M')
|
376
|
+
end
|
377
|
+
end
|
378
|
+
private_constant :SettableValue
|
379
|
+
|
380
|
+
# ClickOptions encapsulates click option logic
|
381
|
+
class ClickOptions
|
382
|
+
attr_reader :keys, :options
|
383
|
+
|
384
|
+
def initialize(keys, options)
|
385
|
+
@keys = keys
|
386
|
+
@options = options
|
387
|
+
end
|
388
|
+
|
389
|
+
def coords?
|
390
|
+
options[:x] && options[:y]
|
391
|
+
end
|
392
|
+
|
393
|
+
def coords
|
394
|
+
[options[:x], options[:y]]
|
395
|
+
end
|
396
|
+
|
397
|
+
def empty?
|
398
|
+
keys.empty? && !coords?
|
340
399
|
end
|
341
400
|
end
|
401
|
+
private_constant :ClickOptions
|
342
402
|
end
|
@@ -3,8 +3,8 @@
|
|
3
3
|
class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
|
4
4
|
def set_file(value) # rubocop:disable Naming/AccessorMethodName
|
5
5
|
super(value)
|
6
|
-
rescue ::Selenium::WebDriver::Error::ExpectedError =>
|
7
|
-
if
|
6
|
+
rescue ::Selenium::WebDriver::Error::ExpectedError => err
|
7
|
+
if err.message =~ /File not found : .+\n.+/m
|
8
8
|
raise ArgumentError, "Selenium < 3.14 with remote Chrome doesn't support multiple file upload"
|
9
9
|
end
|
10
10
|
raise
|
@@ -13,7 +13,7 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
|
|
13
13
|
def drag_to(element)
|
14
14
|
return super unless self[:draggable] == 'true'
|
15
15
|
|
16
|
-
scroll_if_needed {
|
16
|
+
scroll_if_needed { browser_action.click_and_hold(native).perform }
|
17
17
|
driver.execute_script HTML5_DRAG_DROP_SCRIPT, self, element
|
18
18
|
end
|
19
19
|
|
@@ -7,7 +7,7 @@ class Capybara::Selenium::MarionetteNode < Capybara::Selenium::Node
|
|
7
7
|
if tag_name == 'tr'
|
8
8
|
warn 'You are attempting to click a table row which has issues in geckodriver/marionette - see https://github.com/mozilla/geckodriver/issues/1228. ' \
|
9
9
|
'Your test should probably be clicking on a table cell like a user would. Clicking the first cell in the row instead.'
|
10
|
-
return find_css('th:first-child,td:first-child')[0].click
|
10
|
+
return find_css('th:first-child,td:first-child')[0].click(keys, options)
|
11
11
|
end
|
12
12
|
raise
|
13
13
|
end
|
@@ -26,7 +26,8 @@ class Capybara::Selenium::MarionetteNode < Capybara::Selenium::Node
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def set_file(value) # rubocop:disable Naming/AccessorMethodName
|
29
|
-
|
29
|
+
# By default files are appended so we have to clear here if its multiple and already set
|
30
|
+
native.clear if multiple? && driver.evaluate_script('arguments[0].files', self).any?
|
30
31
|
return super if browser_version >= 62.0
|
31
32
|
|
32
33
|
# Workaround lack of support for multiple upload by uploading one at a time
|
@@ -44,25 +45,30 @@ class Capybara::Selenium::MarionetteNode < Capybara::Selenium::Node
|
|
44
45
|
|
45
46
|
def send_keys(*args)
|
46
47
|
# https://github.com/mozilla/geckodriver/issues/846
|
47
|
-
return super(*args.map { |arg| arg == :space ? ' ' : arg }) if args.none? { |
|
48
|
+
return super(*args.map { |arg| arg == :space ? ' ' : arg }) if args.none? { |arg| arg.is_a? Array }
|
48
49
|
|
49
50
|
native.click
|
50
|
-
|
51
|
-
args.each do |keys|
|
51
|
+
args.each_with_object(browser_action) do |keys, actions|
|
52
52
|
_send_keys(keys, actions)
|
53
|
-
end
|
54
|
-
actions.perform
|
53
|
+
end.perform
|
55
54
|
end
|
56
55
|
|
57
56
|
def drag_to(element)
|
58
57
|
return super unless (browser_version >= 62.0) && (self[:draggable] == 'true')
|
59
58
|
|
60
|
-
scroll_if_needed {
|
59
|
+
scroll_if_needed { browser_action.click_and_hold(native).perform }
|
61
60
|
driver.execute_script HTML5_DRAG_DROP_SCRIPT, self, element
|
62
61
|
end
|
63
62
|
|
64
63
|
private
|
65
64
|
|
65
|
+
def click_with_options(click_options)
|
66
|
+
# Firefox/marionette has an issue clicking with offset near viewport edge
|
67
|
+
# scroll element to middle just in case
|
68
|
+
scroll_to_center if click_options.coords?
|
69
|
+
super
|
70
|
+
end
|
71
|
+
|
66
72
|
def _send_keys(keys, actions, down_keys = nil)
|
67
73
|
case keys
|
68
74
|
when String
|
data/lib/capybara/server.rb
CHANGED
@@ -31,7 +31,7 @@ module Capybara
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def reset_error!
|
34
|
-
middleware.
|
34
|
+
middleware.clear_error
|
35
35
|
end
|
36
36
|
|
37
37
|
def error
|
@@ -49,7 +49,7 @@ module Capybara
|
|
49
49
|
if res.is_a?(Net::HTTPSuccess) || res.is_a?(Net::HTTPRedirection)
|
50
50
|
return res.body == app.object_id.to_s
|
51
51
|
end
|
52
|
-
rescue SystemCallError
|
52
|
+
rescue SystemCallError, Net::ReadTimeout, OpenSSL::SSL::SSLError
|
53
53
|
false
|
54
54
|
end
|
55
55
|
|
@@ -3,8 +3,20 @@
|
|
3
3
|
module Capybara
|
4
4
|
class Server
|
5
5
|
class AnimationDisabler
|
6
|
+
def self.selector_for(css_or_bool)
|
7
|
+
case css_or_bool
|
8
|
+
when String
|
9
|
+
css_or_bool
|
10
|
+
when true
|
11
|
+
'*'
|
12
|
+
else
|
13
|
+
raise CapybaraError, 'Capybara.disable_animation supports either a String (the css selector to disable) or a boolean'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
6
17
|
def initialize(app)
|
7
18
|
@app = app
|
19
|
+
@disable_markup = DISABLE_MARKUP_TEMPLATE % AnimationDisabler.selector_for(Capybara.disable_animation)
|
8
20
|
end
|
9
21
|
|
10
22
|
def call(env)
|
@@ -20,18 +32,20 @@ module Capybara
|
|
20
32
|
|
21
33
|
private
|
22
34
|
|
35
|
+
attr_reader :disable_markup
|
36
|
+
|
23
37
|
def html_content?
|
24
38
|
!!(@headers['Content-Type'] =~ /html/)
|
25
39
|
end
|
26
40
|
|
27
41
|
def insert_disable(html)
|
28
|
-
html.sub(%r{(</head>)},
|
42
|
+
html.sub(%r{(</head>)}, disable_markup + '\\1')
|
29
43
|
end
|
30
44
|
|
31
|
-
|
45
|
+
DISABLE_MARKUP_TEMPLATE = <<~HTML
|
32
46
|
<script defer>(typeof jQuery !== 'undefined') && (jQuery.fx.off = true);</script>
|
33
47
|
<style>
|
34
|
-
|
48
|
+
%s {
|
35
49
|
transition: none !important;
|
36
50
|
animation-duration: 0s !important;
|
37
51
|
animation-delay: 0s !important;
|