unpoly-rails 0.36.2 → 0.37.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.

Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/README.md +1 -1
  4. data/dist/unpoly.js +137 -73
  5. data/dist/unpoly.min.js +3 -3
  6. data/lib/assets/javascripts/unpoly/bus.coffee +2 -2
  7. data/lib/assets/javascripts/unpoly/dom/extract_plan.coffee +4 -2
  8. data/lib/assets/javascripts/unpoly/dom.coffee +26 -12
  9. data/lib/assets/javascripts/unpoly/form.coffee +19 -1
  10. data/lib/assets/javascripts/unpoly/layout.coffee +1 -1
  11. data/lib/assets/javascripts/unpoly/link.coffee +9 -2
  12. data/lib/assets/javascripts/unpoly/modal.coffee +1 -0
  13. data/lib/assets/javascripts/unpoly/popup.coffee +1 -0
  14. data/lib/assets/javascripts/unpoly/syntax.coffee +48 -29
  15. data/lib/assets/javascripts/unpoly/util.coffee +12 -5
  16. data/lib/unpoly/rails/version.rb +1 -1
  17. data/package.json +1 -1
  18. data/spec_app/Gemfile.lock +1 -1
  19. data/spec_app/app/assets/javascripts/integration_test.coffee +4 -0
  20. data/spec_app/app/controllers/replace_test_controller.rb +5 -0
  21. data/spec_app/app/views/pages/start.erb +4 -0
  22. data/spec_app/app/views/replace_test/_nav.erb +6 -0
  23. data/spec_app/app/views/replace_test/page1.erb +14 -0
  24. data/spec_app/app/views/replace_test/page2.erb +14 -0
  25. data/spec_app/config/routes.rb +1 -0
  26. data/spec_app/spec/javascripts/helpers/reset_path.js.coffee +1 -2
  27. data/spec_app/spec/javascripts/up/dom_spec.js.coffee +69 -1
  28. data/spec_app/spec/javascripts/up/feedback_spec.js.coffee +5 -3
  29. data/spec_app/spec/javascripts/up/history_spec.js.coffee +174 -153
  30. data/spec_app/spec/javascripts/up/link_spec.js.coffee +77 -19
  31. data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +1 -1
  32. data/spec_app/spec/javascripts/up/util_spec.js.coffee +34 -0
  33. metadata +6 -2
@@ -10,211 +10,243 @@ describe 'up.history', ->
10
10
 
11
11
  describe 'up.history.url', ->
12
12
 
13
- describeCapability 'canPushState', ->
14
-
15
- it 'does not strip a trailing slash from the current URL', ->
16
- history.replaceState?({}, 'title', '/host/path/')
17
- expect(up.history.url()).toEqualUrl('/host/path/')
13
+ it 'does not strip a trailing slash from the current URL', ->
14
+ history.replaceState?({}, 'title', '/host/path/')
15
+ expect(up.history.url()).toEqualUrl('/host/path/')
18
16
 
19
17
  describe 'up.history.isUrl', ->
20
18
 
21
- describeCapability 'canPushState', ->
22
-
23
- it 'returns true if the given path is the current URL', ->
24
- history.replaceState?({}, 'title', '/host/path/')
25
- expect(up.history.isUrl('/host/path/')).toBe(true)
19
+ it 'returns true if the given path is the current URL', ->
20
+ history.replaceState?({}, 'title', '/host/path/')
21
+ expect(up.history.isUrl('/host/path/')).toBe(true)
26
22
 
27
- it 'returns false if the given path is not the current URL', ->
28
- history.replaceState?({}, 'title', '/host/path/')
29
- expect(up.history.isUrl('/host/other-path/')).toBe(false)
23
+ it 'returns false if the given path is not the current URL', ->
24
+ history.replaceState?({}, 'title', '/host/path/')
25
+ expect(up.history.isUrl('/host/other-path/')).toBe(false)
30
26
 
31
- it 'returns true if the given full URL is the current URL', ->
32
- history.replaceState?({}, 'title', '/host/path/')
33
- expect(up.history.isUrl("http://#{location.host}/host/path/")).toBe(true)
27
+ it 'returns true if the given full URL is the current URL', ->
28
+ history.replaceState?({}, 'title', '/host/path/')
29
+ expect(up.history.isUrl("http://#{location.host}/host/path/")).toBe(true)
34
30
 
35
- it 'returns true if the given path is the current URL, but without a trailing slash', ->
36
- history.replaceState?({}, 'title', '/host/path/')
37
- expect(up.history.isUrl('/host/path')).toBe(true)
31
+ it 'returns true if the given path is the current URL, but without a trailing slash', ->
32
+ history.replaceState?({}, 'title', '/host/path/')
33
+ expect(up.history.isUrl('/host/path')).toBe(true)
38
34
 
39
- it 'returns true if the given path is the current URL, but with a trailing slash', ->
40
- history.replaceState?({}, 'title', '/host/path')
41
- expect(up.history.isUrl('/host/path/')).toBe(true)
35
+ it 'returns true if the given path is the current URL, but with a trailing slash', ->
36
+ history.replaceState?({}, 'title', '/host/path')
37
+ expect(up.history.isUrl('/host/path/')).toBe(true)
42
38
 
43
39
  describe 'unobtrusive behavior', ->
44
40
 
45
- describe '[up-back]', ->
41
+ describe 'back button', ->
46
42
 
47
- describeCapability 'canPushState', ->
43
+ it 'calls destructor functions when destroying compiled elements (bugfix)', (done) ->
44
+ waitForBrowser = 70
48
45
 
49
- it 'sets an [up-href] attribute to the previous URL and sets the up-restore-scroll attribute to "true"', ->
50
- up.history.push('/one')
51
- up.history.push('/two')
52
- $element = up.hello(affix('a[href="/three"][up-back]').text('text'))
53
- expect($element.attr('href')).toEqualUrl('/three')
54
- expect($element.attr('up-href')).toEqualUrl('/one')
55
- expect($element.attr('up-restore-scroll')).toBe('')
56
- expect($element.attr('up-follow')).toBe('')
46
+ # By default, up.history will replace the <body> tag when
47
+ # the user presses the back-button. We reconfigure this
48
+ # so we don't lose the Jasmine runner interface.
49
+ up.history.config.popTargets = ['.container']
57
50
 
58
- it 'does not overwrite an existing up-href or up-restore-scroll attribute'
51
+ constructorSpy = jasmine.createSpy('constructor')
52
+ destructorSpy = jasmine.createSpy('destructor')
53
+
54
+ up.compiler '.example', ($example) ->
55
+ constructorSpy()
56
+ return destructorSpy
57
+
58
+ up.history.push('/one')
59
+ up.history.push('/two')
60
+
61
+ $container = affix('.container')
62
+ $example = $container.affix('.example')
63
+ up.hello($example)
64
+
65
+ expect(constructorSpy).toHaveBeenCalled()
66
+
67
+ history.back()
68
+ u.setTimer waitForBrowser, =>
69
+ expect(location.pathname).toEqual('/one')
70
+
71
+ @respondWith "<div class='container'>restored container text</div>"
72
+
73
+ u.nextFrame ->
74
+ expect(destructorSpy).toHaveBeenCalled()
75
+ done()
59
76
 
60
- it 'does not set an up-href attribute if there is no previous URL'
61
77
 
62
- describeFallback 'canPushState', ->
78
+ describe '[up-back]', ->
79
+
80
+ it 'sets an [up-href] attribute to the previous URL and sets the up-restore-scroll attribute to "true"', ->
81
+ up.history.push('/one')
82
+ up.history.push('/two')
83
+ $element = up.hello(affix('a[href="/three"][up-back]').text('text'))
84
+ expect($element.attr('href')).toEqualUrl('/three')
85
+ expect($element.attr('up-href')).toEqualUrl('/one')
86
+ expect($element.attr('up-restore-scroll')).toBe('')
87
+ expect($element.attr('up-follow')).toBe('')
88
+
89
+ it 'does not overwrite an existing up-href or up-restore-scroll attribute'
63
90
 
64
- it 'does not change the element', ->
65
- $element = up.hello(affix('a[href="/three"][up-back]').text('text'))
66
- expect($element.attr('up-href')).toBeUndefined()
91
+ it 'does not set an up-href attribute if there is no previous URL'
67
92
 
68
93
  describe 'scroll restoration', ->
69
94
 
70
- describeCapability 'canPushState', ->
95
+ afterEach ->
96
+ $('.viewport').remove()
71
97
 
72
- afterEach ->
73
- $('.viewport').remove()
98
+ it 'restores the scroll position of viewports when the user hits the back button', (done) ->
74
99
 
75
- it 'restores the scroll position of viewports when the user hits the back button', (done) ->
100
+ longContentHtml = """
101
+ <div class="viewport" style="width: 100px; height: 100px; overflow-y: scroll">
102
+ <div class="content" style="height: 1000px"></div>
103
+ </div>
104
+ """
76
105
 
77
- longContentHtml = """
78
- <div class="viewport" style="width: 100px; height: 100px; overflow-y: scroll">
79
- <div class="content" style="height: 1000px"></div>
80
- </div>
81
- """
106
+ respond = => @respondWith(longContentHtml)
82
107
 
83
- respond = => @respondWith(longContentHtml)
108
+ $viewport = $(longContentHtml).appendTo(document.body)
84
109
 
85
- $viewport = $(longContentHtml).appendTo(document.body)
110
+ up.layout.config.viewports = ['.viewport']
111
+ up.history.config.popTargets = ['.viewport']
86
112
 
87
- up.layout.config.viewports = ['.viewport']
88
- up.history.config.popTargets = ['.viewport']
113
+ up.replace('.content', '/one')
114
+ respond()
89
115
 
90
- up.replace('.content', '/one')
91
- respond()
116
+ $viewport.scrollTop(50)
92
117
 
93
- $viewport.scrollTop(50)
118
+ up.replace('.content', '/two')
119
+ respond()
94
120
 
95
- up.replace('.content', '/two')
96
- respond()
121
+ $('.viewport').scrollTop(150)
97
122
 
98
- $('.viewport').scrollTop(150)
123
+ up.replace('.content', '/three')
124
+ respond()
125
+ $('.viewport').scrollTop(250)
99
126
 
100
- up.replace('.content', '/three')
101
- respond()
102
- $('.viewport').scrollTop(250)
127
+ history.back()
128
+ u.setTimer 50, ->
129
+ respond() # we need to respond since we've never requested /two with the popTarget
130
+ expect($('.viewport').scrollTop()).toBe(150)
103
131
 
104
132
  history.back()
105
133
  u.setTimer 50, ->
106
- respond() # we need to respond since we've never requested /two with the popTarget
107
- expect($('.viewport').scrollTop()).toBe(150)
134
+ respond() # we need to respond since we've never requested /one with the popTarget
135
+ expect($('.viewport').scrollTop()).toBe(50)
108
136
 
109
- history.back()
137
+ history.forward()
110
138
  u.setTimer 50, ->
111
- respond() # we need to respond since we've never requested /one with the popTarget
112
- expect($('.viewport').scrollTop()).toBe(50)
139
+ # No need to respond since we requested /two with the popTarget
140
+ # when we went backwards
141
+ expect($('.viewport').scrollTop()).toBe(150)
113
142
 
114
143
  history.forward()
115
144
  u.setTimer 50, ->
116
- # No need to respond since we requested /two with the popTarget
117
- # when we went backwards
118
- expect($('.viewport').scrollTop()).toBe(150)
119
-
120
- history.forward()
121
- u.setTimer 50, ->
122
- respond() # we need to respond since we've never requested /three with the popTarget
123
- expect($('.viewport').scrollTop()).toBe(250)
124
- done()
125
-
126
- it 'restores the scroll position of two viewports marked with [up-viewport], but not configured in up.layout.config (bugfix)', (done) ->
127
- up.history.config.popTargets = ['.container']
128
-
129
- html = """
130
- <div class="container">
131
- <div class="viewport1" up-viewport style="width: 100px; height: 100px; overflow-y: scroll">
132
- <div class="content1" style="height: 5000px">content1</div>
133
- </div>
134
- <div class="viewport2" up-viewport style="width: 100px; height: 100px; overflow-y: scroll">
135
- <div class="content2" style="height: 5000px">content2</div>
136
- </div>
145
+ respond() # we need to respond since we've never requested /three with the popTarget
146
+ expect($('.viewport').scrollTop()).toBe(250)
147
+ done()
148
+
149
+ it 'restores the scroll position of two viewports marked with [up-viewport], but not configured in up.layout.config (bugfix)', (done) ->
150
+ up.history.config.popTargets = ['.container']
151
+
152
+ html = """
153
+ <div class="container">
154
+ <div class="viewport1" up-viewport style="width: 100px; height: 100px; overflow-y: scroll">
155
+ <div class="content1" style="height: 5000px">content1</div>
137
156
  </div>
138
- """
157
+ <div class="viewport2" up-viewport style="width: 100px; height: 100px; overflow-y: scroll">
158
+ <div class="content2" style="height: 5000px">content2</div>
159
+ </div>
160
+ </div>
161
+ """
139
162
 
140
- respond = => @respondWith(html)
163
+ respond = => @respondWith(html)
141
164
 
142
- $screen = affix('.screen')
143
- $screen.html(html)
165
+ $screen = affix('.screen')
166
+ $screen.html(html)
144
167
 
145
- up.replace('.content1, .content2', '/one', reveal: false)
146
- respond()
168
+ up.replace('.content1, .content2', '/one', reveal: false)
169
+ respond()
147
170
 
148
- $('.viewport1').scrollTop(3000)
149
- $('.viewport2').scrollTop(3050)
171
+ $('.viewport1').scrollTop(3000)
172
+ $('.viewport2').scrollTop(3050)
150
173
 
151
- expect('.viewport1').toBeScrolledTo(3000)
152
- expect('.viewport2').toBeScrolledTo(3050)
174
+ expect('.viewport1').toBeScrolledTo(3000)
175
+ expect('.viewport2').toBeScrolledTo(3050)
153
176
 
154
- up.replace('.content1, .content2', '/two', reveal: false)
155
- respond()
177
+ up.replace('.content1, .content2', '/two', reveal: false)
178
+ respond()
156
179
 
157
- u.setTimer 50, ->
180
+ u.setTimer 50, ->
158
181
 
159
- expect(location.href).toEqualUrl('/two')
182
+ expect(location.href).toEqualUrl('/two')
160
183
 
161
- history.back()
184
+ history.back()
162
185
 
163
- u.setTimer 50, ->
164
- # we need to respond since we've never requested the original URL with the popTarget
165
- respond()
186
+ u.setTimer 50, ->
187
+ # we need to respond since we've never requested the original URL with the popTarget
188
+ respond()
166
189
 
167
- u.nextFrame ->
168
- expect('.viewport1').toBeScrolledTo(3000)
169
- expect('.viewport2').toBeScrolledTo(3050)
170
- done()
190
+ u.nextFrame ->
191
+ expect('.viewport1').toBeScrolledTo(3000)
192
+ expect('.viewport2').toBeScrolledTo(3050)
193
+ done()
171
194
 
172
195
 
173
196
  describe 'events', ->
174
197
 
175
- describeCapability 'canPushState', ->
198
+ it 'emits up:history:* events as the user goes forwards and backwards through history', (done) ->
199
+ up.proxy.config.cacheSize = 0
200
+ up.history.config.popTargets = ['.viewport']
176
201
 
177
- it 'emits up:history:* events as the user goes forwards and backwards through history', (done) ->
178
- up.proxy.config.cacheSize = 0
179
- up.history.config.popTargets = ['.viewport']
202
+ affix('.viewport .content')
203
+ respond = =>
204
+ @respondWith """
205
+ <div class="viewport">
206
+ <div class="content">content</div>
207
+ </div>
208
+ """
180
209
 
181
- affix('.viewport .content')
182
- respond = =>
183
- @respondWith """
184
- <div class="viewport">
185
- <div class="content">content</div>
186
- </div>
187
- """
210
+ events = []
211
+ u.each ['up:history:pushed', 'up:history:restored'], (eventName) ->
212
+ up.on eventName, (event) ->
213
+ events.push [eventName, event.url]
188
214
 
189
- events = []
190
- u.each ['up:history:pushed', 'up:history:restored'], (eventName) ->
191
- up.on eventName, (event) ->
192
- events.push [eventName, event.url]
215
+ normalize = up.history.normalizeUrl
193
216
 
194
- normalize = up.history.normalizeUrl
217
+ up.replace('.content', '/one')
218
+ respond()
195
219
 
196
- up.replace('.content', '/one')
197
- respond()
220
+ expect(events).toEqual [
221
+ ['up:history:pushed', normalize('/one')]
222
+ ]
198
223
 
199
- expect(events).toEqual [
200
- ['up:history:pushed', normalize('/one')]
201
- ]
224
+ up.replace('.content', '/two')
225
+ respond()
202
226
 
203
- up.replace('.content', '/two')
204
- respond()
227
+ expect(events).toEqual [
228
+ ['up:history:pushed', normalize('/one')]
229
+ ['up:history:pushed', normalize('/two')]
230
+ ]
205
231
 
206
- expect(events).toEqual [
207
- ['up:history:pushed', normalize('/one')]
208
- ['up:history:pushed', normalize('/two')]
209
- ]
232
+ up.replace('.content', '/three')
233
+ respond()
234
+
235
+ expect(events).toEqual [
236
+ ['up:history:pushed', normalize('/one')]
237
+ ['up:history:pushed', normalize('/two')]
238
+ ['up:history:pushed', normalize('/three')]
239
+ ]
210
240
 
211
- up.replace('.content', '/three')
241
+ history.back()
242
+ u.setTimer 50, ->
212
243
  respond()
213
244
 
214
245
  expect(events).toEqual [
215
246
  ['up:history:pushed', normalize('/one')]
216
247
  ['up:history:pushed', normalize('/two')]
217
248
  ['up:history:pushed', normalize('/three')]
249
+ ['up:history:restored', normalize('/two')]
218
250
  ]
219
251
 
220
252
  history.back()
@@ -226,9 +258,10 @@ describe 'up.history', ->
226
258
  ['up:history:pushed', normalize('/two')]
227
259
  ['up:history:pushed', normalize('/three')]
228
260
  ['up:history:restored', normalize('/two')]
261
+ ['up:history:restored', normalize('/one')]
229
262
  ]
230
263
 
231
- history.back()
264
+ history.forward()
232
265
  u.setTimer 50, ->
233
266
  respond()
234
267
 
@@ -238,11 +271,12 @@ describe 'up.history', ->
238
271
  ['up:history:pushed', normalize('/three')]
239
272
  ['up:history:restored', normalize('/two')]
240
273
  ['up:history:restored', normalize('/one')]
274
+ ['up:history:restored', normalize('/two')]
241
275
  ]
242
276
 
243
277
  history.forward()
244
278
  u.setTimer 50, ->
245
- respond()
279
+ respond() # we need to respond since we've never requested /three with the popTarget
246
280
 
247
281
  expect(events).toEqual [
248
282
  ['up:history:pushed', normalize('/one')]
@@ -251,20 +285,7 @@ describe 'up.history', ->
251
285
  ['up:history:restored', normalize('/two')]
252
286
  ['up:history:restored', normalize('/one')]
253
287
  ['up:history:restored', normalize('/two')]
288
+ ['up:history:restored', normalize('/three')]
254
289
  ]
255
290
 
256
- history.forward()
257
- u.setTimer 50, ->
258
- respond() # we need to respond since we've never requested /three with the popTarget
259
-
260
- expect(events).toEqual [
261
- ['up:history:pushed', normalize('/one')]
262
- ['up:history:pushed', normalize('/two')]
263
- ['up:history:pushed', normalize('/three')]
264
- ['up:history:restored', normalize('/two')]
265
- ['up:history:restored', normalize('/one')]
266
- ['up:history:restored', normalize('/two')]
267
- ['up:history:restored', normalize('/three')]
268
- ]
269
-
270
- done()
291
+ done()
@@ -365,34 +365,92 @@ describe 'up.link', ->
365
365
  expect($('.target')).toHaveText('new text')
366
366
  expect(location.pathname).toEqual('/other/path')
367
367
 
368
- it 'prefers to update a container in the same layer as the clicked link', (done) ->
369
- up.motion.config.enabled = false
368
+ describe 'choice of target layer', ->
370
369
 
371
- $popupOpener = affix('a[href="/popup"]')
372
- up.popup.attach($popupOpener, html: "<div class='target'>old popup text</div>", target: '.target')
373
- affix('.document').affix('.target').text('old document text')
370
+ beforeEach ->
371
+ up.motion.config.enabled = false
374
372
 
375
- $linkInDocument = affix('a[href="/foo"][up-target=".target"]')
376
- $linkInPopup = $('.up-popup').affix('a[href="/bar"][up-target=".target"]')
373
+ it 'prefers to update a container in the same layer as the clicked link', (done) ->
374
+ affix('.document').affix('.target').text('old document text')
375
+ up.modal.extract('.target', "<div class='target'>old modal text</div>")
377
376
 
378
- expect($('.document .target')).toHaveText('old document text')
379
- expect($('.up-popup .target')).toHaveText('old popup text')
377
+ u.nextFrame =>
378
+ expect($('.document .target')).toHaveText('old document text')
379
+ expect($('.up-modal .target')).toHaveText('old modal text')
380
380
 
381
- u.nextFrame =>
381
+ $linkInModal = $('.up-modal').affix('a[href="/bar"][up-target=".target"]')
382
+ Trigger.clickSequence($linkInModal)
382
383
 
383
- Trigger.clickSequence($linkInPopup)
384
+ u.nextFrame =>
385
+ @respondWith '<div class="target">new text from modal link</div>'
384
386
 
385
- u.nextFrame =>
386
- @respondWith '<div class="target">new text from popup link</div>'
387
+ expect($('.document .target')).toHaveText('old document text')
388
+ expect($('.up-modal .target')).toHaveText('new text from modal link')
387
389
 
388
- expect($('.document .target')).toHaveText('old document text')
389
- expect($('.up-popup .target')).toHaveText('new text from popup link')
390
- Trigger.clickSequence($linkInDocument)
390
+ done()
391
+
392
+ describe 'with [up-layer] modifier', ->
393
+
394
+ it 'allows to name a layer for the update', (done) ->
395
+ affix('.document').affix('.target').text('old document text')
396
+ up.modal.extract('.target', "<div class='target'>old modal text</div>", sticky: true)
391
397
 
392
398
  u.nextFrame =>
393
- @respondWith '<div class="target">new text from document link</div>'
394
- expect($('.document .target')).toHaveText('new text from document link')
395
- done()
399
+ expect($('.document .target')).toHaveText('old document text')
400
+ expect($('.up-modal .target')).toHaveText('old modal text')
401
+
402
+ $linkInModal = $('.up-modal').affix('a[href="/bar"][up-target=".target"][up-layer="page"]')
403
+ Trigger.clickSequence($linkInModal)
404
+
405
+ u.nextFrame =>
406
+ @respondWith '<div class="target">new text from modal link</div>'
407
+
408
+ expect($('.document .target')).toHaveText('new text from modal link')
409
+ expect($('.up-modal .target')).toHaveText('old modal text')
410
+
411
+ done()
412
+
413
+ it 'ignores [up-layer] if the server responds with a non-200 status code', (done) ->
414
+ affix('.document').affix('.target').text('old document text')
415
+ up.modal.extract('.target', "<div class='target'>old modal text</div>", sticky: true)
416
+
417
+ u.nextFrame =>
418
+ expect($('.document .target')).toHaveText('old document text')
419
+ expect($('.up-modal .target')).toHaveText('old modal text')
420
+
421
+ $linkInModal = $('.up-modal').affix('a[href="/bar"][up-target=".target"][up-fail-target=".target"][up-layer="page"]')
422
+ Trigger.clickSequence($linkInModal)
423
+
424
+ u.nextFrame =>
425
+ @respondWith
426
+ responseText: '<div class="target">new failure text from modal link</div>'
427
+ status: 500
428
+
429
+ expect($('.document .target')).toHaveText('old document text')
430
+ expect($('.up-modal .target')).toHaveText('new failure text from modal link')
431
+
432
+ done()
433
+
434
+ it 'allows to name a layer for a non-200 response using an [up-fail-layer] modifier', (done) ->
435
+ affix('.document').affix('.target').text('old document text')
436
+ up.modal.extract('.target', "<div class='target'>old modal text</div>", sticky: true)
437
+
438
+ u.nextFrame =>
439
+ expect($('.document .target')).toHaveText('old document text')
440
+ expect($('.up-modal .target')).toHaveText('old modal text')
441
+
442
+ $linkInModal = $('.up-modal').affix('a[href="/bar"][up-target=".target"][up-fail-target=".target"][up-fail-layer="page"]')
443
+ Trigger.clickSequence($linkInModal)
444
+
445
+ u.nextFrame =>
446
+ @respondWith
447
+ responseText: '<div class="target">new failure text from modal link</div>'
448
+ status: 500
449
+
450
+ expect($('.document .target')).toHaveText('new failure text from modal link')
451
+ expect($('.up-modal .target')).toHaveText('old modal text')
452
+
453
+ done()
396
454
 
397
455
  describe 'with [up-fail-target] modifier', ->
398
456
 
@@ -28,7 +28,7 @@ describe 'up.syntax', ->
28
28
  up.destroy('.container')
29
29
  expect(destructor).toHaveBeenCalled()
30
30
 
31
- it 'allows compilers to return an array of functions to clal when the compiled element is destroyed', ->
31
+ it 'allows compilers to return an array of functions to all when the compiled element is destroyed', ->
32
32
  destructor1 = jasmine.createSpy('destructor1')
33
33
  destructor2 = jasmine.createSpy('destructor2')
34
34
  up.compiler '.child', ($element) ->
@@ -688,6 +688,40 @@ describe 'up.util', ->
688
688
  array = [1, [2, [3,4]], 5]
689
689
  expect(u.flatten(array)).toEqual([1, 2, [3, 4], 5])
690
690
 
691
+ describe 'up.util.renameKey', ->
692
+
693
+ it 'renames a key in the given property', ->
694
+ object = { a: 'a value', b: 'b value'}
695
+ u.renameKey(object, 'a', 'c')
696
+ expect(object.a).toBeUndefined()
697
+ expect(object.b).toBe('b value')
698
+ expect(object.c).toBe('a value')
699
+
700
+ describe 'up.util.findWithSelf', ->
701
+
702
+ it 'finds the selector in descendants of the given element', ->
703
+ $container = affix('div')
704
+ $child1 = $container.affix('div.match')
705
+ $child2 = $container.affix('div')
706
+ $child2Child1 = $child2.affix('div.match')
707
+ matches = u.findWithSelf($container, '.match')
708
+ expect(matches).toEqual [$child1.get(0), $child2Child1.get(0)]
709
+
710
+ it 'finds the element itself if the element matches the given selector', ->
711
+ $container = affix('div.match')
712
+ $child1 = $container.affix('div')
713
+ $child1Child1 = $child1.affix('div.match')
714
+ matches = u.findWithSelf($container, '.match')
715
+ expect(matches).toEqual [$container.get(0), $child1Child1.get(0)]
716
+
717
+ it 'returns multiple matches in the same subtree', ->
718
+ $container = affix('div.match')
719
+ $child1 = $container.affix('div')
720
+ $child2 = $container.affix('div.match')
721
+ $child2Child1 = $child2.affix('div.match')
722
+ matches = u.findWithSelf($container, '.match')
723
+ expect(matches).toEqual [$container.get(0), $child2.get(0), $child2Child1.get(0)]
724
+
691
725
  describe 'up.util.memoize', ->
692
726
 
693
727
  it 'returns a function that calls the memoized function', ->
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unpoly-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.36.2
4
+ version: 0.37.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Henning Koch
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-03 00:00:00.000000000 Z
11
+ date: 2017-08-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -157,6 +157,7 @@ files:
157
157
  - spec_app/app/controllers/form_test/basics_controller.rb
158
158
  - spec_app/app/controllers/form_test/uploads_controller.rb
159
159
  - spec_app/app/controllers/pages_controller.rb
160
+ - spec_app/app/controllers/replace_test_controller.rb
160
161
  - spec_app/app/helpers/application_helper.rb
161
162
  - spec_app/app/mailers/.keep
162
163
  - spec_app/app/models/concerns/.keep
@@ -173,6 +174,9 @@ files:
173
174
  - spec_app/app/views/form_test/uploads/new.erb
174
175
  - spec_app/app/views/layouts/integration_test.erb
175
176
  - spec_app/app/views/pages/start.erb
177
+ - spec_app/app/views/replace_test/_nav.erb
178
+ - spec_app/app/views/replace_test/page1.erb
179
+ - spec_app/app/views/replace_test/page2.erb
176
180
  - spec_app/bin/bundle
177
181
  - spec_app/bin/rails
178
182
  - spec_app/bin/rake