unpoly-rails 0.27.3 → 0.28.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of unpoly-rails might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -1
- data/dist/unpoly.css +29 -21
- data/dist/unpoly.js +291 -134
- data/dist/unpoly.min.css +1 -1
- data/dist/unpoly.min.js +3 -3
- data/lib/assets/javascripts/unpoly.js.coffee +1 -0
- data/lib/assets/javascripts/unpoly/browser.js.coffee +22 -5
- data/lib/assets/javascripts/unpoly/bus.js.coffee +1 -1
- data/lib/assets/javascripts/unpoly/flow.js.coffee +9 -6
- data/lib/assets/javascripts/unpoly/form.js.coffee +46 -49
- data/lib/assets/javascripts/unpoly/history.js.coffee +2 -2
- data/lib/assets/javascripts/unpoly/layout.js.coffee +3 -3
- data/lib/assets/javascripts/unpoly/modal.js.coffee +30 -2
- data/lib/assets/javascripts/unpoly/motion.js.coffee +6 -6
- data/lib/assets/javascripts/unpoly/navigation.js.coffee +1 -1
- data/lib/assets/javascripts/unpoly/popup.js.coffee +2 -2
- data/lib/assets/javascripts/unpoly/proxy.js.coffee +5 -9
- data/lib/assets/javascripts/unpoly/syntax.js.coffee +10 -6
- data/lib/assets/javascripts/unpoly/toast.js.coffee +66 -0
- data/lib/assets/javascripts/unpoly/tooltip.js.coffee +1 -1
- data/lib/assets/javascripts/unpoly/util.js.coffee +47 -21
- data/lib/assets/stylesheets/unpoly/toast.css.sass +33 -0
- data/lib/unpoly/rails/version.rb +1 -1
- data/spec_app/Gemfile.lock +1 -1
- data/spec_app/app/assets/javascripts/integration_test.coffee +1 -0
- data/spec_app/app/assets/stylesheets/integration_test.sass +14 -0
- data/spec_app/app/controllers/application_controller.rb +8 -0
- data/spec_app/app/controllers/error_test_controller.rb +5 -0
- data/spec_app/app/views/error_test/trigger.erb +72 -0
- data/spec_app/app/views/error_test/unexpected_response.erb +3 -0
- data/spec_app/app/views/pages/start.erb +4 -0
- data/spec_app/config/routes.rb +2 -0
- data/spec_app/spec/javascripts/helpers/last_request.js.coffee +1 -1
- data/spec_app/spec/javascripts/up/browser_spec.js.coffee +62 -0
- data/spec_app/spec/javascripts/up/form_spec.js.coffee +116 -3
- data/spec_app/spec/javascripts/up/link_spec.js.coffee +2 -2
- data/spec_app/spec/javascripts/up/modal_spec.js.coffee +88 -4
- data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +27 -1
- data/spec_app/spec/javascripts/up/util_spec.js.coffee +12 -0
- metadata +8 -3
- data/lib/assets/stylesheets/unpoly/error.css.sass +0 -21
@@ -0,0 +1,72 @@
|
|
1
|
+
<div class="example">
|
2
|
+
|
3
|
+
<h2>Link to unexpected response</h2>
|
4
|
+
|
5
|
+
<p>
|
6
|
+
<a href="/error_test/unexpected_response" up-target=".link-target">Link</a>
|
7
|
+
</p>
|
8
|
+
|
9
|
+
<div class="area link-target">
|
10
|
+
.link-target
|
11
|
+
</div>
|
12
|
+
|
13
|
+
</div>
|
14
|
+
|
15
|
+
<div class="example">
|
16
|
+
|
17
|
+
<h2>Form to unexpected response</h2>
|
18
|
+
|
19
|
+
<%= form_tag "/error_test/unexpected_response", 'up-target' => '.form-target' do %>
|
20
|
+
|
21
|
+
<p>
|
22
|
+
<input type="text" name="param" value="param-value">
|
23
|
+
</p>
|
24
|
+
|
25
|
+
<p>
|
26
|
+
<button type="submit">Submit</button>
|
27
|
+
</p>
|
28
|
+
|
29
|
+
<% end %>
|
30
|
+
|
31
|
+
<div class="area form-target">
|
32
|
+
.form-target
|
33
|
+
</div>
|
34
|
+
|
35
|
+
</div>
|
36
|
+
|
37
|
+
<script>
|
38
|
+
|
39
|
+
if (false) {
|
40
|
+
|
41
|
+
var html = '<!DOCTYPE html>\
|
42
|
+
<html>\
|
43
|
+
<head>\
|
44
|
+
<title>Integration test - Unpoly</title>\
|
45
|
+
<link rel="stylesheet" media="all" href="/assets/integration_test-6697c9a85c007956bf460adc74ba46bc.css" />\
|
46
|
+
<meta name="csrf-param" content="authenticity_token" />\
|
47
|
+
<meta name="csrf-token" content="CYCqVYIr0wbjErWnupydWZiO8WMqRn+z4BlHN4g4o3T3UN4hW2sDfQXFT3/ygZt41m0jo/YEl6syohm8b8DsYw==" />\
|
48
|
+
</head>\
|
49
|
+
<body>\
|
50
|
+
<div class="page">\
|
51
|
+
<div class="unexpected-response">\
|
52
|
+
unexpected response\
|
53
|
+
</div>\
|
54
|
+
\
|
55
|
+
</div>\
|
56
|
+
</body>\
|
57
|
+
</html>';
|
58
|
+
|
59
|
+
var selector = '.linx-target';
|
60
|
+
|
61
|
+
up.fail(['Could not find selector %s in response %s', selector, html], {
|
62
|
+
inspect: {
|
63
|
+
label: 'Open response',
|
64
|
+
callback: function () {
|
65
|
+
alert("callback")
|
66
|
+
}
|
67
|
+
}
|
68
|
+
});
|
69
|
+
|
70
|
+
}
|
71
|
+
|
72
|
+
</script>
|
data/spec_app/config/routes.rb
CHANGED
@@ -5,6 +5,8 @@ Rails.application.routes.draw do
|
|
5
5
|
|
6
6
|
get 'binding_test/:action', controller: 'binding_test'
|
7
7
|
get 'css_test/:action', controller: 'css_test'
|
8
|
+
get 'error_test/:action', controller: 'error_test'
|
9
|
+
post 'error_test/:action', controller: 'error_test'
|
8
10
|
|
9
11
|
namespace :form_test do
|
10
12
|
resource :basic, only: [:new, :create]
|
@@ -0,0 +1,62 @@
|
|
1
|
+
describe 'up.browser', ->
|
2
|
+
|
3
|
+
u = up.util
|
4
|
+
|
5
|
+
describe 'Javascript functions', ->
|
6
|
+
|
7
|
+
describe 'up.browser.loadPage', ->
|
8
|
+
|
9
|
+
afterEach ->
|
10
|
+
# We're preventing the form to be submitted during tests,
|
11
|
+
# so we need to remove it manually after each example.
|
12
|
+
$('form.up-page-loader').remove()
|
13
|
+
|
14
|
+
describe 'for GET requests', ->
|
15
|
+
|
16
|
+
it 'sets location.href to the given URL', ->
|
17
|
+
hrefSetter = up.browser.knife.mock('setLocationHref')
|
18
|
+
up.browser.loadPage('/foo')
|
19
|
+
expect(hrefSetter).toHaveBeenCalledWith('/foo')
|
20
|
+
|
21
|
+
it 'encodes { data } params into the URL', ->
|
22
|
+
hrefSetter = up.browser.knife.mock('setLocationHref')
|
23
|
+
up.browser.loadPage('/foo', data: { param1: 'param1 value', param2: 'param2 value' })
|
24
|
+
expect(hrefSetter).toHaveBeenCalledWith('/foo?param1=param1%20value¶m2=param2%20value')
|
25
|
+
|
26
|
+
describe 'for POST requests', ->
|
27
|
+
|
28
|
+
it 'creates a form, adds all { data } params a hidden fields and submits the form', ->
|
29
|
+
submitForm = up.browser.knife.mock('submitForm')
|
30
|
+
up.browser.loadPage('/foo', method: 'post', data: { param1: 'param1 value', param2: 'param2 value' })
|
31
|
+
expect(submitForm).toHaveBeenCalled()
|
32
|
+
$form = $('form.up-page-loader')
|
33
|
+
expect($form).toExist()
|
34
|
+
expect($form.attr('action')).toEqual('/foo')
|
35
|
+
expect($form.attr('method')).toEqual('post')
|
36
|
+
expect($form.find('input[name="param1"][value="param1 value"]')).toExist()
|
37
|
+
expect($form.find('input[name="param2"][value="param2 value"]')).toExist()
|
38
|
+
|
39
|
+
it 'submits the Rails CSRF token as another hidden field', ->
|
40
|
+
submitForm = up.browser.knife.mock('submitForm')
|
41
|
+
spyOn(up.rails, 'csrfField').and.returnValue
|
42
|
+
name: 'authenticity-param-name',
|
43
|
+
value: 'authenticity-token'
|
44
|
+
up.browser.loadPage('/foo', method: 'post')
|
45
|
+
expect(submitForm).toHaveBeenCalled()
|
46
|
+
$form = $('form.up-page-loader')
|
47
|
+
$tokenInput = $form.find('input[name="authenticity-param-name"]')
|
48
|
+
expect($tokenInput).toExist()
|
49
|
+
expect($tokenInput.val()).toEqual('authenticity-token')
|
50
|
+
|
51
|
+
u.each ['PUT', 'PATCH', 'DELETE'], (method) ->
|
52
|
+
|
53
|
+
describe "for #{method} requests", ->
|
54
|
+
|
55
|
+
it "uses a POST form and sends the actual method as a { _method } param", ->
|
56
|
+
submitForm = up.browser.knife.mock('submitForm')
|
57
|
+
up.browser.loadPage('/foo', method: method)
|
58
|
+
expect(submitForm).toHaveBeenCalled()
|
59
|
+
$form = $('form.up-page-loader')
|
60
|
+
expect($form).toExist()
|
61
|
+
expect($form.attr('method')).toEqual('post')
|
62
|
+
expect($form.find('input[name="_method"]').val()).toEqual(method.toLowerCase())
|
@@ -6,6 +6,9 @@ describe 'up.form', ->
|
|
6
6
|
|
7
7
|
describe 'up.observe', ->
|
8
8
|
|
9
|
+
beforeEach ->
|
10
|
+
up.form.config.observeDelay = 0
|
11
|
+
|
9
12
|
changeEvents = if up.browser.canInputEvent()
|
10
13
|
# Actually we only need `input`, but we want to notice
|
11
14
|
# if another script manually triggers `change` on the element.
|
@@ -16,9 +19,9 @@ describe 'up.form', ->
|
|
16
19
|
# on the element.
|
17
20
|
['input', 'change', 'keypress', 'paste', 'cut', 'click', 'propertychange']
|
18
21
|
|
19
|
-
|
22
|
+
describe 'when the first argument is a form field', ->
|
20
23
|
|
21
|
-
|
24
|
+
u.each changeEvents, (eventName) ->
|
22
25
|
|
23
26
|
it "runs the callback when the input receives a '#{eventName}' event and the value changed", (done) ->
|
24
27
|
$input = affix('input[value="old-value"]')
|
@@ -58,7 +61,107 @@ describe 'up.form', ->
|
|
58
61
|
$input.trigger(eventName)
|
59
62
|
expect(callback.calls.count()).toEqual(2)
|
60
63
|
|
61
|
-
describe 'when the first argument is a
|
64
|
+
describe 'when the first argument is a checkbox', ->
|
65
|
+
|
66
|
+
it 'runs the callback when the checkbox changes its checked state', (done) ->
|
67
|
+
$form = affix('form')
|
68
|
+
$checkbox = $form.affix('input[type="checkbox"]')
|
69
|
+
callback = jasmine.createSpy('change callback')
|
70
|
+
up.observe($checkbox, callback)
|
71
|
+
expect($checkbox.is(':checked')).toBe(false)
|
72
|
+
$checkbox.get(0).click()
|
73
|
+
u.nextFrame ->
|
74
|
+
expect($checkbox.is(':checked')).toBe(true)
|
75
|
+
expect(callback.calls.count()).toEqual(1)
|
76
|
+
$checkbox.get(0).click()
|
77
|
+
u.nextFrame ->
|
78
|
+
expect($checkbox.is(':checked')).toBe(false)
|
79
|
+
expect(callback.calls.count()).toEqual(2)
|
80
|
+
done()
|
81
|
+
|
82
|
+
it 'runs the callback when the checkbox is toggled by clicking its label', (done) ->
|
83
|
+
$form = affix('form')
|
84
|
+
$checkbox = $form.affix('input#tick[type="checkbox"]')
|
85
|
+
$label = $form.affix('label[for="tick"]').text('tick label')
|
86
|
+
callback = jasmine.createSpy('change callback')
|
87
|
+
up.observe($checkbox, callback)
|
88
|
+
expect($checkbox.is(':checked')).toBe(false)
|
89
|
+
$label.get(0).click()
|
90
|
+
u.nextFrame ->
|
91
|
+
expect($checkbox.is(':checked')).toBe(true)
|
92
|
+
expect(callback.calls.count()).toEqual(1)
|
93
|
+
$label.get(0).click()
|
94
|
+
u.nextFrame ->
|
95
|
+
expect($checkbox.is(':checked')).toBe(false)
|
96
|
+
expect(callback.calls.count()).toEqual(2)
|
97
|
+
done()
|
98
|
+
|
99
|
+
describe 'when the first argument is a radio button group', ->
|
100
|
+
|
101
|
+
it 'runs the callback when the group changes its selection', (done) ->
|
102
|
+
$form = affix('form')
|
103
|
+
$radio1 = $form.affix('input[type="radio"][name="group"][value="1"]')
|
104
|
+
$radio2 = $form.affix('input[type="radio"][name="group"][value="2"]')
|
105
|
+
$group = $radio1.add($radio2)
|
106
|
+
callback = jasmine.createSpy('change callback')
|
107
|
+
up.observe($group, callback)
|
108
|
+
expect($radio1.is(':checked')).toBe(false)
|
109
|
+
$radio1.get(0).click()
|
110
|
+
u.nextFrame ->
|
111
|
+
expect($radio1.is(':checked')).toBe(true)
|
112
|
+
expect(callback.calls.count()).toEqual(1)
|
113
|
+
$radio2.get(0).click()
|
114
|
+
u.nextFrame ->
|
115
|
+
expect($radio1.is(':checked')).toBe(false)
|
116
|
+
expect(callback.calls.count()).toEqual(2)
|
117
|
+
done()
|
118
|
+
|
119
|
+
it "runs the callbacks when a radio button is selected or deselected by clicking a label in the group", (done) ->
|
120
|
+
$form = affix('form')
|
121
|
+
$radio1 = $form.affix('input#radio1[type="radio"][name="group"][value="1"]')
|
122
|
+
$radio1Label = $form.affix('label[for="radio1"]').text('label 1')
|
123
|
+
$radio2 = $form.affix('input#radio2[type="radio"][name="group"][value="2"]')
|
124
|
+
$radio2Label = $form.affix('label[for="radio2"]').text('label 2')
|
125
|
+
$group = $radio1.add($radio2)
|
126
|
+
callback = jasmine.createSpy('change callback')
|
127
|
+
up.observe($group, callback)
|
128
|
+
expect($radio1.is(':checked')).toBe(false)
|
129
|
+
$radio1Label.get(0).click()
|
130
|
+
u.nextFrame ->
|
131
|
+
expect($radio1.is(':checked')).toBe(true)
|
132
|
+
expect(callback.calls.count()).toEqual(1)
|
133
|
+
$radio2Label.get(0).click()
|
134
|
+
u.nextFrame ->
|
135
|
+
expect($radio1.is(':checked')).toBe(false)
|
136
|
+
expect(callback.calls.count()).toEqual(2)
|
137
|
+
done()
|
138
|
+
|
139
|
+
it "takes the group's initial selected value into account", (done) ->
|
140
|
+
$form = affix('form')
|
141
|
+
$radio1 = $form.affix('input[type="radio"][name="group"][value="1"][checked="checked"]')
|
142
|
+
$radio2 = $form.affix('input[type="radio"][name="group"][value="2"]')
|
143
|
+
$group = $radio1.add($radio2)
|
144
|
+
callback = jasmine.createSpy('change callback')
|
145
|
+
up.observe($group, callback)
|
146
|
+
expect($radio1.is(':checked')).toBe(true)
|
147
|
+
expect($radio2.is(':checked')).toBe(false)
|
148
|
+
$radio1.get(0).click()
|
149
|
+
u.nextFrame ->
|
150
|
+
# Since the radio button was already checked, the click doesn't do anything
|
151
|
+
expect($radio1.is(':checked')).toBe(true)
|
152
|
+
expect($radio2.is(':checked')).toBe(false)
|
153
|
+
# Since the radio button was already checked, clicking it again won't trigger the callback
|
154
|
+
expect(callback.calls.count()).toEqual(0)
|
155
|
+
$radio2.get(0).click()
|
156
|
+
u.nextFrame ->
|
157
|
+
expect($radio1.is(':checked')).toBe(false)
|
158
|
+
expect($radio2.is(':checked')).toBe(true)
|
159
|
+
expect(callback.calls.count()).toEqual(1)
|
160
|
+
done()
|
161
|
+
|
162
|
+
describe 'when the first argument is a form', ->
|
163
|
+
|
164
|
+
u.each changeEvents, (eventName) ->
|
62
165
|
|
63
166
|
it "runs the callback when any of the form's inputs receives a '#{eventName}' event and the value changed", (done) ->
|
64
167
|
$form = affix('form')
|
@@ -82,6 +185,16 @@ describe 'up.form', ->
|
|
82
185
|
expect(callback).not.toHaveBeenCalled()
|
83
186
|
done()
|
84
187
|
|
188
|
+
# it 'runs the callback only once when a radio button group changes its selection', ->
|
189
|
+
# $form = affix('form')
|
190
|
+
# $radio1 = $form.affix('input[type="radio"][name="group"][value="1"][checked="checked"]')
|
191
|
+
# $radio2 = $form.affix('input[type="radio"][name="group"][value="2"]')
|
192
|
+
# callback = jasmine.createSpy('change callback')
|
193
|
+
# up.observe($form, callback)
|
194
|
+
# $radio2.get(0).click()
|
195
|
+
# u.nextFrame ->
|
196
|
+
# expect(callback.calls.count()).toEqual(1)
|
197
|
+
|
85
198
|
describe 'up.submit', ->
|
86
199
|
|
87
200
|
describeCapability 'canPushState', ->
|
@@ -395,7 +395,7 @@ describe 'up.link', ->
|
|
395
395
|
|
396
396
|
it 'morphs between the old and new target element', (done) ->
|
397
397
|
affix('.target.old')
|
398
|
-
$link = affix('a[href="/path"][up-target=".target"][up-transition="cross-fade"][up-duration="
|
398
|
+
$link = affix('a[href="/path"][up-target=".target"][up-transition="cross-fade"][up-duration="500"][up-easing="linear"]')
|
399
399
|
$link.click()
|
400
400
|
@respondWith '<div class="target new">new text</div>'
|
401
401
|
|
@@ -405,7 +405,7 @@ describe 'up.link', ->
|
|
405
405
|
expect($newGhost).toExist()
|
406
406
|
expect(u.opacity($oldGhost)).toBeAround(1, 0.15)
|
407
407
|
expect(u.opacity($newGhost)).toBeAround(0, 0.15)
|
408
|
-
u.setTimer
|
408
|
+
u.setTimer 250, ->
|
409
409
|
expect(u.opacity($oldGhost)).toBeAround(0.5, 0.15)
|
410
410
|
expect(u.opacity($newGhost)).toBeAround(0.5, 0.15)
|
411
411
|
done()
|
@@ -318,13 +318,24 @@ describe 'up.modal', ->
|
|
318
318
|
|
319
319
|
describe 'up.modal.close', ->
|
320
320
|
|
321
|
-
it 'closes a currently open modal'
|
321
|
+
it 'closes a currently open modal', (done) ->
|
322
|
+
up.modal.extract('.modal', '<div class="modal">Modal content</div>')
|
322
323
|
|
323
|
-
|
324
|
+
modalContent = $('.modal')
|
325
|
+
expect(modalContent).toBeInDOM()
|
324
326
|
|
325
|
-
|
327
|
+
up.modal.close().then ->
|
328
|
+
expect(modalContent).not.toBeInDOM()
|
329
|
+
done()
|
326
330
|
|
327
|
-
it '
|
331
|
+
it 'does nothing if no modal is open', (done) ->
|
332
|
+
wasClosed = false
|
333
|
+
up.on 'up:modal:close', ->
|
334
|
+
wasClosed = true
|
335
|
+
|
336
|
+
up.modal.close().then ->
|
337
|
+
expect(wasClosed).toBe(false)
|
338
|
+
done()
|
328
339
|
|
329
340
|
describe 'unobtrusive behavior', ->
|
330
341
|
|
@@ -443,6 +454,79 @@ describe 'up.modal', ->
|
|
443
454
|
expect(wasClosed).toBe(false)
|
444
455
|
expect(wasDefaultPrevented).toBe(false)
|
445
456
|
|
457
|
+
describe 'template behavior', ->
|
458
|
+
|
459
|
+
it 'closes the modal on close icon click', (done) ->
|
460
|
+
wasClosed = false
|
461
|
+
up.modal.extract('.modal', '<div class="modal">Modal content</div>', animation: false)
|
462
|
+
|
463
|
+
closeIcon = $('.up-modal-close')
|
464
|
+
up.on 'up:modal:close', ->
|
465
|
+
wasClosed = true
|
466
|
+
|
467
|
+
closeIcon.click()
|
468
|
+
u.nextFrame ->
|
469
|
+
expect(wasClosed).toBe(true)
|
470
|
+
done()
|
471
|
+
|
472
|
+
it 'closes the modal on backdrop click', (done) ->
|
473
|
+
wasClosed = false
|
474
|
+
up.modal.extract('.modal', '<div class="modal">Modal content</div>', animation: false)
|
475
|
+
|
476
|
+
backdrop = $('.up-modal-backdrop')
|
477
|
+
up.on 'up:modal:close', ->
|
478
|
+
wasClosed = true
|
479
|
+
|
480
|
+
backdrop.click()
|
481
|
+
u.nextFrame ->
|
482
|
+
expect(wasClosed).toBe(true)
|
483
|
+
done()
|
484
|
+
|
485
|
+
it 'closes the modal when the user presses the escape key', (done) ->
|
486
|
+
wasClosed = false
|
487
|
+
up.modal.extract('.modal', '<div class="modal">Modal content</div>', animation: false)
|
488
|
+
up.on 'up:modal:close', ->
|
489
|
+
wasClosed = true
|
490
|
+
|
491
|
+
escapeEvent = $.Event('keydown', keyCode: 27)
|
492
|
+
$('body').trigger(escapeEvent)
|
493
|
+
u.nextFrame ->
|
494
|
+
expect(wasClosed).toBe(true)
|
495
|
+
done()
|
496
|
+
|
497
|
+
describe 'when opened with { closable: false }', ->
|
498
|
+
|
499
|
+
it 'does not render a close icon', ->
|
500
|
+
up.modal.extract('.modal', '<div class="modal">Modal content</div>', animation: false, closable: false)
|
501
|
+
|
502
|
+
modal = $('.up-modal')
|
503
|
+
expect(modal).not.toContainElement('.up-modal-close')
|
504
|
+
|
505
|
+
it 'does not close the modal on backdrop click', (done) ->
|
506
|
+
wasClosed = false
|
507
|
+
up.modal.extract('.modal', '<div class="modal">Modal content</div>', animation: false, closable: false)
|
508
|
+
|
509
|
+
backdrop = $('.up-modal-backdrop')
|
510
|
+
up.on 'up:modal:close', ->
|
511
|
+
wasClosed = true
|
512
|
+
|
513
|
+
backdrop.click()
|
514
|
+
u.nextFrame ->
|
515
|
+
expect(wasClosed).toBe(false)
|
516
|
+
done()
|
517
|
+
|
518
|
+
it 'does not close the modal when the user presses the escape key', (done) ->
|
519
|
+
wasClosed = false
|
520
|
+
up.modal.extract('.modal', '<div class="modal">Modal content</div>', animation: false, closable: false)
|
521
|
+
up.on 'up:modal:close', ->
|
522
|
+
wasClosed = true
|
523
|
+
|
524
|
+
escapeEvent = $.Event('keydown', keyCode: 27)
|
525
|
+
$('body').trigger(escapeEvent)
|
526
|
+
u.nextFrame ->
|
527
|
+
expect(wasClosed).toBe(false)
|
528
|
+
done()
|
529
|
+
|
446
530
|
describe 'when replacing content', ->
|
447
531
|
|
448
532
|
beforeEach ->
|