unpoly-rails 0.20.0 → 0.21.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.

@@ -4,6 +4,6 @@ module Unpoly
4
4
  # The current version of the unpoly-rails gem.
5
5
  # This version number is also used for releases of the Unpoly
6
6
  # frontend code.
7
- VERSION = '0.20.0'
7
+ VERSION = '0.21.0'
8
8
  end
9
9
  end
@@ -32,12 +32,21 @@ describe 'up.flow', ->
32
32
  expect($('.after')).toHaveText('old-after')
33
33
  done()
34
34
 
35
- it 'sends an X-Up-Target HTTP headers along with the request', ->
35
+ it 'sends an X-Up-Target HTTP header along with the request', ->
36
36
  up.replace('.middle', '/path')
37
37
  request = @lastRequest()
38
- console.log(request.requestHeaders)
39
38
  expect(request.requestHeaders['X-Up-Target']).toEqual('.middle')
40
39
 
40
+ it 'returns a promise that will be resolved once the server response was received and the fragments were swapped', ->
41
+ resolution = jasmine.createSpy()
42
+ promise = up.replace('.middle', '/path')
43
+ promise.then(resolution)
44
+ expect(resolution).not.toHaveBeenCalled()
45
+ expect($('.middle')).toHaveText('old-middle')
46
+ @respond()
47
+ expect(resolution).toHaveBeenCalled()
48
+ expect($('.middle')).toHaveText('new-middle')
49
+
41
50
  describe 'with { data } option', ->
42
51
 
43
52
  it "uses the given params as a non-GET request's payload", ->
@@ -457,6 +466,78 @@ describe 'up.flow', ->
457
466
  expect($(elements.get(0)).text()).toEqual('text')
458
467
  expect($(elements.get(1)).text()).toEqual('')
459
468
 
469
+ describe 'with { transition } option', ->
470
+
471
+ it 'morphs between the old and new element', (done) ->
472
+ affix('.element').text('version 1')
473
+ up.extract('.element', '<div class="element">version 2</div>', transition: 'cross-fade', duration: 50)
474
+
475
+ $ghost1 = $('.element.up-ghost:contains("version 1")')
476
+ expect($ghost1).toHaveLength(1)
477
+ expect($ghost1.css('opacity')).toBeAround(1.0, 0.1)
478
+
479
+ $ghost2 = $('.element.up-ghost:contains("version 2")')
480
+ expect($ghost2).toHaveLength(1)
481
+ expect($ghost2.css('opacity')).toBeAround(0.0, 0.1)
482
+
483
+ @setTimer 40, ->
484
+ expect($ghost1.css('opacity')).toBeAround(0.0, 0.2)
485
+ expect($ghost2.css('opacity')).toBeAround(1.0, 0.2)
486
+ done()
487
+
488
+ it 'marks the old fragment and its ghost as .up-destroying during the transition', ->
489
+ affix('.element').text('version 1')
490
+ up.extract('.element', '<div class="element">version 2</div>', transition: 'cross-fade', duration: 200)
491
+
492
+ $version1 = $('.element:not(.up-ghost):contains("version 1")')
493
+ $version1Ghost = $('.element.up-ghost:contains("version 1")')
494
+ expect($version1).toHaveLength(1)
495
+ expect($version1Ghost).toHaveLength(1)
496
+ expect($version1).toHaveClass('up-destroying')
497
+ expect($version1Ghost).toHaveClass('up-destroying')
498
+
499
+ $version2 = $('.element:not(.up-ghost):contains("version 2")')
500
+ $version2Ghost = $('.element.up-ghost:contains("version 2")')
501
+ expect($version2).toHaveLength(1)
502
+ expect($version2Ghost).toHaveLength(1)
503
+ expect($version2).not.toHaveClass('up-destroying')
504
+ expect($version2Ghost).not.toHaveClass('up-destroying')
505
+
506
+ it 'cancels an existing transition by instantly jumping to the last frame', ->
507
+ affix('.element').text('version 1')
508
+ up.extract('.element', '<div class="element">version 2</div>', transition: 'cross-fade', duration: 200)
509
+
510
+ $ghost1 = $('.element.up-ghost:contains("version 1")')
511
+ expect($ghost1).toHaveLength(1)
512
+ expect($ghost1.css('opacity')).toBeAround(1.0, 0.1)
513
+
514
+ $ghost2 = $('.element.up-ghost:contains("version 2")')
515
+ expect($ghost2).toHaveLength(1)
516
+ expect($ghost2.css('opacity')).toBeAround(0.0, 0.1)
517
+
518
+ up.extract('.element', '<div class="element">version 3</div>', transition: 'cross-fade', duration: 200)
519
+
520
+ $ghost1 = $('.element.up-ghost:contains("version 1")')
521
+ expect($ghost1).toHaveLength(0)
522
+
523
+ $ghost2 = $('.element.up-ghost:contains("version 2")')
524
+ expect($ghost2).toHaveLength(1)
525
+ expect($ghost2.css('opacity')).toBeAround(1.0, 0.1)
526
+
527
+ $ghost3 = $('.element.up-ghost:contains("version 3")')
528
+ expect($ghost3).toHaveLength(1)
529
+ expect($ghost3.css('opacity')).toBeAround(0.0, 0.1)
530
+
531
+ it 'delays the resolution of the returned promise until the transition is over', (done) ->
532
+ affix('.element').text('version 1')
533
+ resolution = jasmine.createSpy()
534
+ promise = up.extract('.element', '<div class="element">version 2</div>', transition: 'cross-fade', duration: 30)
535
+ promise.then(resolution)
536
+ expect(resolution).not.toHaveBeenCalled()
537
+ @setTimer 50, ->
538
+ expect(resolution).toHaveBeenCalled()
539
+ done()
540
+
460
541
  describe 'handling of [up-keep] elements', ->
461
542
 
462
543
  squish = (string) ->
@@ -509,8 +590,6 @@ describe 'up.flow', ->
509
590
  up.extract '.keeper', "<div class='keeper' up-keep>new-inside</div>"
510
591
  expect($('.keeper')).toHaveText('old-inside')
511
592
 
512
- it "does not compile the kept element a second time"
513
-
514
593
  it "only emits an event up:fragment:kept, but not an event up:fragment:inserted", ->
515
594
  insertedListener = jasmine.createSpy('subscriber to up:fragment:inserted')
516
595
  up.on('up:fragment:inserted', insertedListener)
@@ -282,12 +282,12 @@ describe 'up.form', ->
282
282
  expect($labels[0]).not.toHaveText('Validation message')
283
283
  expect($labels[1]).toHaveText('Validation message')
284
284
 
285
- describe '[up-toggle]', ->
285
+ describe '[up-switch]', ->
286
286
 
287
287
  describe 'on a select', ->
288
288
 
289
289
  beforeEach ->
290
- @$select = affix('select[up-toggle=".target"]')
290
+ @$select = affix('select[up-switch=".target"]')
291
291
  @$blankOption = @$select.affix('option').text('<Please select something>').val('')
292
292
  @$fooOption = @$select.affix('option[value="foo"]').text('Foo')
293
293
  @$barOption = @$select.affix('option[value="bar"]').text('Bar')
@@ -331,7 +331,7 @@ describe 'up.form', ->
331
331
  describe 'on a checkbox', ->
332
332
 
333
333
  beforeEach ->
334
- @$checkbox = affix('input[type="checkbox"][value="1"][up-toggle=".target"]')
334
+ @$checkbox = affix('input[type="checkbox"][value="1"][up-switch=".target"]')
335
335
 
336
336
  it "shows the target element iff its up-show-for attribute is :checked and the checkbox is checked", ->
337
337
  $target = affix('.target[up-show-for=":checked"]')
@@ -372,10 +372,10 @@ describe 'up.form', ->
372
372
 
373
373
  beforeEach ->
374
374
  @$buttons = affix('.radio-buttons')
375
- @$blankButton = @$buttons.affix('input[type="radio"][name="group"][up-toggle=".target"]').val('')
376
- @$fooButton = @$buttons.affix('input[type="radio"][name="group"][up-toggle=".target"]').val('foo')
377
- @$barButton = @$buttons.affix('input[type="radio"][name="group"][up-toggle=".target"]').val('bar')
378
- @$bazkButton = @$buttons.affix('input[type="radio"][name="group"][up-toggle=".target"]').val('baz')
375
+ @$blankButton = @$buttons.affix('input[type="radio"][name="group"][up-switch=".target"]').val('')
376
+ @$fooButton = @$buttons.affix('input[type="radio"][name="group"][up-switch=".target"]').val('foo')
377
+ @$barButton = @$buttons.affix('input[type="radio"][name="group"][up-switch=".target"]').val('bar')
378
+ @$bazkButton = @$buttons.affix('input[type="radio"][name="group"][up-switch=".target"]').val('baz')
379
379
 
380
380
  it "shows the target element iff its up-show-for attribute contains the selected button value", ->
381
381
  $target = affix('.target[up-show-for="something bar other"]')
@@ -433,7 +433,7 @@ describe 'up.form', ->
433
433
  describe 'on a text input', ->
434
434
 
435
435
  beforeEach ->
436
- @$textInput = affix('input[type="text"][up-toggle=".target"]')
436
+ @$textInput = affix('input[type="text"][up-switch=".target"]')
437
437
 
438
438
  it "shows the target element iff its up-show-for attribute contains the input value", ->
439
439
  $target = affix('.target[up-show-for="something bar other"]')
@@ -92,6 +92,53 @@ describe 'up.syntax', ->
92
92
  expect($baz.attr('up-keep')).toBeMissing()
93
93
  expect($bam.attr('up-keep')).toEqual('.partner')
94
94
 
95
+ describe 'with { priority } option', ->
96
+
97
+ it 'runs compilers with higher priority first', ->
98
+ traces = []
99
+ up.compiler '.element', { priority: 1 }, -> traces.push('foo')
100
+ up.compiler '.element', { priority: 2 }, -> traces.push('bar')
101
+ up.compiler '.element', { priority: 0 }, -> traces.push('baz')
102
+ up.compiler '.element', { priority: 3 }, -> traces.push('bam')
103
+ up.compiler '.element', { priority: -1 }, -> traces.push('qux')
104
+ up.hello(affix('.element'))
105
+ expect(traces).toEqual ['qux', 'baz', 'foo', 'bar', 'bam']
106
+
107
+ it 'considers priority-less compilers to be priority zero', ->
108
+ traces = []
109
+ up.compiler '.element', { priority: 1 }, -> traces.push('foo')
110
+ up.compiler '.element', -> traces.push('bar')
111
+ up.compiler '.element', { priority: -1 }, -> traces.push('baz')
112
+ up.hello(affix('.element'))
113
+ expect(traces).toEqual ['baz', 'bar', 'foo']
114
+
115
+ it 'runs two compilers with the same priority in the order in which they were registered', ->
116
+ traces = []
117
+ up.compiler '.element', { priority: 1 }, -> traces.push('foo')
118
+ up.compiler '.element', { priority: 1 }, -> traces.push('bar')
119
+ up.hello(affix('.element'))
120
+ expect(traces).toEqual ['foo', 'bar']
121
+
122
+ describe 'up.macro', ->
123
+
124
+ it 'registers compilers that are run before other compilers', ->
125
+ traces = []
126
+ up.compiler '.element', { priority: 10 }, -> traces.push('foo')
127
+ up.compiler '.element', { priority: -1000 }, -> traces.push('bar')
128
+ up.macro '.element', -> traces.push('baz')
129
+ up.hello(affix('.element'))
130
+ expect(traces).toEqual ['baz', 'bar' , 'foo']
131
+
132
+ it 'allows to macros to have priorities of their own', ->
133
+ traces = []
134
+ up.macro '.element', { priority: 1 }, -> traces.push('foo')
135
+ up.macro '.element', { priority: 2 }, -> traces.push('bar')
136
+ up.macro '.element', { priority: 0 }, -> traces.push('baz')
137
+ up.macro '.element', { priority: 3 }, -> traces.push('bam')
138
+ up.macro '.element', { priority: -1 }, -> traces.push('qux')
139
+ up.hello(affix('.element'))
140
+ expect(traces).toEqual ['qux', 'baz', 'foo', 'bar', 'bam']
141
+
95
142
  describe 'up.hello', ->
96
143
 
97
144
  it 'should have tests'
@@ -245,6 +245,82 @@ describe 'up.util', ->
245
245
  string = up.util.requestDataAsQuery({ 'my+key': 'my+value' })
246
246
  expect(string).toEqual('my%2Bkey=my%2Bvalue')
247
247
 
248
+ describe 'up.util.resolvableWhen', ->
249
+
250
+ it 'returns a promise that is resolved when all the given deferreds are resolved', ->
251
+ one = jasmine.createSpy()
252
+ two = jasmine.createSpy()
253
+ both = jasmine.createSpy()
254
+ oneDeferred = $.Deferred()
255
+ oneDeferred.then(one)
256
+ twoDeferred = $.Deferred()
257
+ twoDeferred.then(two)
258
+
259
+ bothDeferred = up.util.resolvableWhen(oneDeferred, twoDeferred)
260
+ bothDeferred.then(both)
261
+
262
+ expect(one).not.toHaveBeenCalled()
263
+ expect(two).not.toHaveBeenCalled()
264
+ expect(both).not.toHaveBeenCalled()
265
+
266
+ oneDeferred.resolve()
267
+ expect(one).toHaveBeenCalled()
268
+ expect(two).not.toHaveBeenCalled()
269
+ expect(both).not.toHaveBeenCalled()
270
+
271
+ twoDeferred.resolve()
272
+ expect(one).toHaveBeenCalled()
273
+ expect(two).toHaveBeenCalled()
274
+ expect(both).toHaveBeenCalled()
275
+
276
+ it 'returns a promise with a .resolve method that resolves the given deferreds', ->
277
+ one = jasmine.createSpy()
278
+ two = jasmine.createSpy()
279
+ both = jasmine.createSpy()
280
+ oneDeferred = $.Deferred()
281
+ oneDeferred.then(one)
282
+ twoDeferred = $.Deferred()
283
+ twoDeferred.then(two)
284
+
285
+ bothDeferred = up.util.resolvableWhen(oneDeferred, twoDeferred)
286
+ bothDeferred.then(both)
287
+
288
+ expect(one).not.toHaveBeenCalled()
289
+ expect(two).not.toHaveBeenCalled()
290
+ expect(both).not.toHaveBeenCalled()
291
+
292
+ bothDeferred.resolve()
293
+ expect(one).toHaveBeenCalled()
294
+ expect(two).toHaveBeenCalled()
295
+ expect(both).toHaveBeenCalled()
296
+
297
+ it 'does not resolve the given deferreds more than once', ->
298
+ oneDeferred = $.Deferred()
299
+ spyOn(oneDeferred, 'resolve')
300
+ bothDeferred = up.util.resolvableWhen(oneDeferred)
301
+
302
+ bothDeferred.resolve()
303
+ bothDeferred.resolve()
304
+
305
+ expect(oneDeferred.resolve.calls.count()).toEqual(1)
306
+
307
+ describe 'bugfix against troublesome jQuery optimization if only one deferred is given', ->
308
+
309
+ it 'does not simply return the given deferred', ->
310
+ oneDeferred = $.Deferred()
311
+ whenDeferred = up.util.resolvableWhen(oneDeferred)
312
+ # This is what $.when returns if only passed a single argument
313
+ expect(whenDeferred).not.toBe(oneDeferred.promise())
314
+ # Cover eventual implementations
315
+ expect(whenDeferred).not.toBe(oneDeferred)
316
+ expect(whenDeferred.promise()).not.toBe(oneDeferred.promise())
317
+ expect(whenDeferred.promise()).not.toBe(oneDeferred)
318
+
319
+ it 'does not create an infinite loop if the given deferred is nested twice and the first nesting is resolved', ->
320
+ oneDeferred = $.Deferred()
321
+ firstNesting = up.util.resolvableWhen(oneDeferred)
322
+ secondNesting = up.util.resolvableWhen(firstNesting)
323
+ expect(-> firstNesting.resolve()).not.toThrowError()
248
324
 
249
325
  describe 'up.util.requestDataAsArray', ->
250
326
 
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.20.0
4
+ version: 0.21.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: 2016-02-26 00:00:00.000000000 Z
11
+ date: 2016-03-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails