unpoly-rails 0.37.0 → 0.50.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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +127 -25
  3. data/LICENSE +1 -1
  4. data/README_RAILS.md +4 -2
  5. data/Rakefile +6 -1
  6. data/dist/unpoly.js +3192 -2198
  7. data/dist/unpoly.min.js +4 -3
  8. data/lib/assets/javascripts/unpoly/browser.coffee +51 -63
  9. data/lib/assets/javascripts/unpoly/bus.coffee +58 -33
  10. data/lib/assets/javascripts/unpoly/classes/cache.coffee +117 -0
  11. data/lib/assets/javascripts/unpoly/{dom → classes}/extract_cascade.coffee +3 -3
  12. data/lib/assets/javascripts/unpoly/{dom → classes}/extract_plan.coffee +1 -1
  13. data/lib/assets/javascripts/unpoly/classes/field_observer.coffee +57 -0
  14. data/lib/assets/javascripts/unpoly/classes/follow_variant.coffee +52 -0
  15. data/lib/assets/javascripts/unpoly/classes/motion_tracker.coffee +95 -0
  16. data/lib/assets/javascripts/unpoly/classes/record.coffee +16 -0
  17. data/lib/assets/javascripts/unpoly/classes/request.coffee +228 -0
  18. data/lib/assets/javascripts/unpoly/classes/response.coffee +138 -0
  19. data/lib/assets/javascripts/unpoly/dom.coffee +151 -142
  20. data/lib/assets/javascripts/unpoly/feedback.coffee +67 -38
  21. data/lib/assets/javascripts/unpoly/form.coffee +156 -139
  22. data/lib/assets/javascripts/unpoly/history.coffee +22 -19
  23. data/lib/assets/javascripts/unpoly/layout.coffee +108 -90
  24. data/lib/assets/javascripts/unpoly/link.coffee +159 -158
  25. data/lib/assets/javascripts/unpoly/log.coffee +5 -5
  26. data/lib/assets/javascripts/unpoly/modal.coffee +93 -81
  27. data/lib/assets/javascripts/unpoly/motion.coffee +291 -250
  28. data/lib/assets/javascripts/unpoly/popup.coffee +67 -53
  29. data/lib/assets/javascripts/unpoly/protocol.coffee +67 -16
  30. data/lib/assets/javascripts/unpoly/proxy.coffee +282 -211
  31. data/lib/assets/javascripts/unpoly/rails.coffee +3 -14
  32. data/lib/assets/javascripts/unpoly/syntax.coffee +54 -49
  33. data/lib/assets/javascripts/unpoly/tooltip.coffee +18 -25
  34. data/lib/assets/javascripts/unpoly/util.coffee +236 -477
  35. data/lib/assets/javascripts/unpoly.coffee +1 -1
  36. data/lib/unpoly/rails/inspector.rb +67 -22
  37. data/lib/unpoly/rails/version.rb +1 -1
  38. data/package.json +1 -1
  39. data/spec_app/Gemfile.lock +13 -13
  40. data/spec_app/app/assets/javascripts/integration_test.coffee +1 -0
  41. data/spec_app/app/assets/javascripts/jasmine_specs.coffee +1 -1
  42. data/spec_app/app/assets/stylesheets/jasmine_specs.sass +10 -0
  43. data/spec_app/app/controllers/binding_test_controller.rb +19 -2
  44. data/spec_app/app/controllers/method_test_controller.rb +16 -0
  45. data/spec_app/app/views/layouts/jasmine_rails/spec_runner.html.erb +20 -0
  46. data/spec_app/app/views/method_test/form_target.erb +17 -0
  47. data/spec_app/app/views/method_test/page1.erb +11 -0
  48. data/spec_app/app/views/method_test/page2.erb +6 -0
  49. data/spec_app/app/views/pages/start.erb +33 -19
  50. data/spec_app/config/initializers/assets.rb +5 -0
  51. data/spec_app/config/routes.rb +3 -0
  52. data/spec_app/spec/controllers/binding_test_controller_spec.rb +82 -27
  53. data/spec_app/spec/javascripts/helpers/agent_detector.coffee +17 -0
  54. data/spec_app/spec/javascripts/helpers/async_sequence.js.coffee +102 -0
  55. data/spec_app/spec/javascripts/helpers/last_request.js.coffee +1 -1
  56. data/spec_app/spec/javascripts/helpers/mock_ajax.js.coffee +5 -2
  57. data/spec_app/spec/javascripts/helpers/promise_state.js +18 -0
  58. data/spec_app/spec/javascripts/helpers/protect_jasmine_runner.coffee +9 -0
  59. data/spec_app/spec/javascripts/helpers/reset_history.js.coffee +22 -0
  60. data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +11 -3
  61. data/spec_app/spec/javascripts/helpers/show_lib_versions.coffee +10 -0
  62. data/spec_app/spec/javascripts/helpers/to_be_error.coffee +5 -0
  63. data/spec_app/spec/javascripts/helpers/to_match_url.coffee +13 -0
  64. data/spec_app/spec/javascripts/helpers/trigger.js.coffee +13 -6
  65. data/spec_app/spec/javascripts/up/browser_spec.js.coffee +92 -33
  66. data/spec_app/spec/javascripts/up/bus_spec.js.coffee +64 -15
  67. data/spec_app/spec/javascripts/up/classes/.keep +0 -0
  68. data/spec_app/spec/javascripts/up/classes/cache_spec.js.coffee +1 -0
  69. data/spec_app/spec/javascripts/up/dom_spec.js.coffee +759 -551
  70. data/spec_app/spec/javascripts/up/feedback_spec.js.coffee +155 -82
  71. data/spec_app/spec/javascripts/up/form_spec.js.coffee +490 -349
  72. data/spec_app/spec/javascripts/up/history_spec.js.coffee +226 -179
  73. data/spec_app/spec/javascripts/up/layout_spec.js.coffee +253 -185
  74. data/spec_app/spec/javascripts/up/link_spec.js.coffee +416 -270
  75. data/spec_app/spec/javascripts/up/modal_spec.js.coffee +459 -330
  76. data/spec_app/spec/javascripts/up/motion_spec.js.coffee +198 -153
  77. data/spec_app/spec/javascripts/up/namespace_spec.js.coffee +9 -0
  78. data/spec_app/spec/javascripts/up/popup_spec.js.coffee +240 -175
  79. data/spec_app/spec/javascripts/up/protocol_spec.js.coffee +38 -0
  80. data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +777 -303
  81. data/spec_app/spec/javascripts/up/rails_spec.js.coffee +24 -8
  82. data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +40 -23
  83. data/spec_app/spec/javascripts/up/tooltip_spec.js.coffee +80 -66
  84. data/spec_app/spec/javascripts/up/util_spec.js.coffee +227 -201
  85. data/spec_app/vendor/asset-libs/es6-promise-4.1.6/es6-promise.auto.js +1159 -0
  86. metadata +30 -7
  87. data/spec_app/spec/javascripts/helpers/reset_path.js.coffee +0 -7
  88. data/spec_app/spec/javascripts/helpers/to_equal_url.coffee +0 -11
@@ -29,78 +29,89 @@ describe 'up.layout', ->
29
29
  afterEach ->
30
30
  @$container.remove()
31
31
 
32
- it 'reveals the given element', ->
32
+ it 'reveals the given element', asyncSpec (next) ->
33
33
  up.reveal(@$elements[0])
34
- # ---------------------
35
- # [0] 0 .......... ch-1
36
- # ---------------------
37
- # [1] ch+0 ...... ch+49
38
- # [2] ch+50 ... ch+5049
39
- expect($(document).scrollTop()).toBe(0)
40
34
 
41
- up.reveal(@$elements[1])
42
- # ---------------------
43
- # [0] 0 .......... ch-1
44
- # [1] ch+0 ...... ch+49
45
- # ---------------------
46
- # [2] ch+50 ... ch+5049
47
- expect($(document).scrollTop()).toBe(50)
35
+ next =>
36
+ # ---------------------
37
+ # [0] 0 .......... ch-1
38
+ # ---------------------
39
+ # [1] ch+0 ...... ch+49
40
+ # [2] ch+50 ... ch+5049
41
+ expect($(document).scrollTop()).toBe(0)
48
42
 
49
- up.reveal(@$elements[2])
50
- # [0] 0 .......... ch-1
51
- # [1] ch+0 ...... ch+49
52
- # ---------------------
53
- # [2] ch+50 ... ch+5049
54
- # ---------------------
55
- expect($(document).scrollTop()).toBe(@clientHeight + 50)
56
-
57
- it "includes the element's top margin in the revealed area", ->
43
+ up.reveal(@$elements[1])
44
+
45
+ next =>
46
+ # ---------------------
47
+ # [0] 0 .......... ch-1
48
+ # [1] ch+0 ...... ch+49
49
+ # ---------------------
50
+ # [2] ch+50 ... ch+5049
51
+ expect($(document).scrollTop()).toBe(50)
52
+
53
+ up.reveal(@$elements[2])
54
+
55
+ next =>
56
+ # [0] 0 .......... ch-1
57
+ # [1] ch+0 ...... ch+49
58
+ # ---------------------
59
+ # [2] ch+50 ... ch+5049
60
+ # ---------------------
61
+ expect($(document).scrollTop()).toBe(@clientHeight + 50)
62
+
63
+ it "includes the element's top margin in the revealed area", asyncSpec (next) ->
58
64
  @$elements[1].css('margin-top': '20px')
59
65
  up.reveal(@$elements[1])
60
- expect($(document).scrollTop()).toBe(50 + 20)
66
+ next => expect($(document).scrollTop()).toBe(50 + 20)
61
67
 
62
- it "includes the element's bottom margin in the revealed area", ->
68
+ it "includes the element's bottom margin in the revealed area", asyncSpec (next) ->
63
69
  @$elements[1].css('margin-bottom': '20px')
64
70
  up.reveal(@$elements[2])
65
- expect($(document).scrollTop()).toBe(@clientHeight + 50 + 20)
66
-
67
- it 'snaps to the top if the space above the future-visible area is smaller than the value of config.snap', ->
71
+ next => expect($(document).scrollTop()).toBe(@clientHeight + 50 + 20)
68
72
 
73
+ it 'snaps to the top if the space above the future-visible area is smaller than the value of config.snap', asyncSpec (next) ->
69
74
  up.layout.config.snap = 30
70
75
 
71
76
  @$elements[0].css(height: '20px')
72
77
 
73
78
  up.reveal(@$elements[2])
74
- # [0] 0 ............ 19
75
- # [1] 20 ........... 69
76
- # ---------------------
77
- # [2] 70 ......... 5069
78
- # ---------------------
79
- expect($(document).scrollTop()).toBe(70)
80
-
81
- # Even though we're revealing the second element, the viewport
82
- # snaps to the top edge.
83
- up.reveal(@$elements[1])
84
- # ---------------------
85
- # [0] 0 ............ 19
86
- # [1] 20 ........... 69
87
- # ---------------------
88
- # [2] 70 ......... 5069
89
- expect($(document).scrollTop()).toBe(0)
90
-
91
- it 'does not snap to the top if it would un-reveal an element at the bottom edge of the screen (bugfix)', ->
79
+
80
+ next =>
81
+ # [0] 0 ............ 19
82
+ # [1] 20 ........... 69
83
+ # ---------------------
84
+ # [2] 70 ......... 5069
85
+ # ---------------------
86
+ expect($(document).scrollTop()).toBe(70)
87
+
88
+ # Even though we're revealing the second element, the viewport
89
+ # snaps to the top edge.
90
+ up.reveal(@$elements[1])
91
+
92
+ next =>
93
+ # ---------------------
94
+ # [0] 0 ............ 19
95
+ # [1] 20 ........... 69
96
+ # ---------------------
97
+ # [2] 70 ......... 5069
98
+ expect($(document).scrollTop()).toBe(0)
99
+
100
+ it 'does not snap to the top if it would un-reveal an element at the bottom edge of the screen (bugfix)', asyncSpec (next) ->
92
101
  up.layout.config.snap = 100
93
102
 
94
103
  up.reveal(@$elements[1])
95
- # ---------------------
96
- # [0] 0 .......... ch-1
97
- # [1] ch+0 ...... ch+49
98
- # ---------------------
99
- # [2] ch+50 ... ch+5049
100
- expect($(document).scrollTop()).toBe(50)
104
+
105
+ next =>
106
+ # ---------------------
107
+ # [0] 0 .......... ch-1
108
+ # [1] ch+0 ...... ch+49
109
+ # ---------------------
110
+ # [2] ch+50 ... ch+5049
111
+ expect($(document).scrollTop()).toBe(50)
101
112
 
102
113
 
103
- it 'scrolls far enough so the element is not obstructed by an element fixed to the top', ->
114
+ it 'scrolls far enough so the element is not obstructed by an element fixed to the top', asyncSpec (next) ->
104
115
  $topNav = affix('[up-fixed=top]').css(
105
116
  position: 'fixed',
106
117
  top: '0',
@@ -110,44 +121,51 @@ describe 'up.layout', ->
110
121
  )
111
122
 
112
123
  up.reveal(@$elements[0], viewport: @viewport)
113
- # ---------------------
114
- # [F] 0 ............ 99
115
- # [0] 0 .......... ch-1
116
- # ---------------------
117
- # [1] ch+0 ...... ch+49
118
- # [2] ch+50 ... ch+5049
119
- expect($(document).scrollTop()).toBe(0) # would need to be -100
120
124
 
121
- up.reveal(@$elements[1])
122
- # ---------------------
123
- # [F] 0 ............ 99
124
- # [0] 00000 ...... ch-1
125
- # [1] ch+0 ...... ch+49
126
- # ---------------------
127
- # [2] ch+50 ... ch+5049
125
+ next =>
126
+ # ---------------------
127
+ # [F] 0 ............ 99
128
+ # [0] 0 .......... ch-1
129
+ # ---------------------
130
+ # [1] ch+0 ...... ch+49
131
+ # [2] ch+50 ... ch+5049
132
+ expect($(document).scrollTop()).toBe(0) # would need to be -100
128
133
 
129
- expect($(document).scrollTop()).toBe(50)
134
+ up.reveal(@$elements[1])
130
135
 
131
- up.reveal(@$elements[2])
132
- # [0] 00000 ...... ch-1
133
- # [1] ch+0 ...... ch+49
134
- # ---------------------
135
- # [F] 0 ............ 99
136
- # [2] ch+50 ... ch+5049
137
- # ----------------
138
- expect($(document).scrollTop()).toBe(@clientHeight + 50 - 100)
136
+ next =>
137
+ # ---------------------
138
+ # [F] 0 ............ 99
139
+ # [0] 00000 ...... ch-1
140
+ # [1] ch+0 ...... ch+49
141
+ # ---------------------
142
+ # [2] ch+50 ... ch+5049
143
+ expect($(document).scrollTop()).toBe(50)
139
144
 
140
- up.reveal(@$elements[1])
141
- # [0] 00000 ...... ch-1
142
- # ---------------------
143
- # [F] 0 ............ 99
144
- # [1] ch+0 ...... ch+49
145
- # [2] ch+50 ... ch+5049
146
- # ----------------
147
- expect($(document).scrollTop()).toBe(@clientHeight + 50 - 100 - 50)
145
+ up.reveal(@$elements[2])
146
+
147
+ next =>
148
+ # [0] 00000 ...... ch-1
149
+ # [1] ch+0 ...... ch+49
150
+ # ---------------------
151
+ # [F] 0 ............ 99
152
+ # [2] ch+50 ... ch+5049
153
+ # ----------------
154
+ expect($(document).scrollTop()).toBe(@clientHeight + 50 - 100)
155
+
156
+ up.reveal(@$elements[1])
157
+
158
+ next =>
159
+ # [0] 00000 ...... ch-1
160
+ # ---------------------
161
+ # [F] 0 ............ 99
162
+ # [1] ch+0 ...... ch+49
163
+ # [2] ch+50 ... ch+5049
164
+ # ----------------
165
+ expect($(document).scrollTop()).toBe(@clientHeight + 50 - 100 - 50)
148
166
 
149
167
 
150
- it 'scrolls far enough so the element is not obstructed by an element fixed to the bottom', ->
168
+ it 'scrolls far enough so the element is not obstructed by an element fixed to the bottom', asyncSpec (next) ->
151
169
  $bottomNav = affix('[up-fixed=bottom]').css(
152
170
  position: 'fixed',
153
171
  bottom: '0',
@@ -157,50 +175,57 @@ describe 'up.layout', ->
157
175
  )
158
176
 
159
177
  up.reveal(@$elements[0])
160
- # ---------------------
161
- # [0] 0 .......... ch-1
162
- # [F] 0 ............ 99
163
- # ---------------------
164
- # [1] ch+0 ...... ch+49
165
- # [2] ch+50 ... ch+5049
166
- expect($(document).scrollTop()).toBe(0)
167
178
 
168
- up.reveal(@$elements[1])
169
- # ---------------------
170
- # [0] 0 .......... ch-1
171
- # [1] ch+0 ...... ch+49
172
- # [F] 0 ............ 99
173
- # ---------------------
174
- # [2] ch+50 ... ch+5049
175
- expect($(document).scrollTop()).toBe(150)
179
+ next =>
180
+ # ---------------------
181
+ # [0] 0 .......... ch-1
182
+ # [F] 0 ............ 99
183
+ # ---------------------
184
+ # [1] ch+0 ...... ch+49
185
+ # [2] ch+50 ... ch+5049
186
+ expect($(document).scrollTop()).toBe(0)
176
187
 
177
- up.reveal(@$elements[2])
178
- # ---------------------
179
- # [0] 0 .......... ch-1
180
- # [1] ch+0 ...... ch+49
181
- # ---------------------
182
- # [2] ch+50 ... ch+5049
183
- # [F] 0 ............ 99
184
- expect($(document).scrollTop()).toBe(@clientHeight + 50)
188
+ up.reveal(@$elements[1])
185
189
 
186
- describe 'with { top: true } option', ->
190
+ next =>
191
+ # ---------------------
192
+ # [0] 0 .......... ch-1
193
+ # [1] ch+0 ...... ch+49
194
+ # [F] 0 ............ 99
195
+ # ---------------------
196
+ # [2] ch+50 ... ch+5049
197
+ expect($(document).scrollTop()).toBe(150)
187
198
 
188
- it 'scrolls the viewport to the first row of the element, even if that element is already fully revealed', ->
199
+ up.reveal(@$elements[2])
189
200
 
201
+ next =>
202
+ # ---------------------
203
+ # [0] 0 .......... ch-1
204
+ # [1] ch+0 ...... ch+49
205
+ # ---------------------
206
+ # [2] ch+50 ... ch+5049
207
+ # [F] 0 ............ 99
208
+ expect($(document).scrollTop()).toBe(@clientHeight + 50)
209
+
210
+ describe 'with { top: true } option', ->
211
+
212
+ it 'scrolls the viewport to the first row of the element, even if that element is already fully revealed', asyncSpec (next) ->
190
213
  @$elements[0].css(height: '20px')
191
214
 
192
215
  up.reveal(@$elements[1], { top: true })
193
- # [0] 0 ............ 19
194
- # [1] 20 ........... 69
195
- # ---------------------
196
- # [2] 70 ......... 5069
197
- # ---------------------
198
- expect($(document).scrollTop()).toBe(20)
216
+
217
+ next =>
218
+ # [0] 0 ............ 19
219
+ # [1] 20 ........... 69
220
+ # ---------------------
221
+ # [2] 70 ......... 5069
222
+ # ---------------------
223
+ expect($(document).scrollTop()).toBe(20)
199
224
 
200
225
 
201
226
  describe 'when the viewport is a container with overflow-y: scroll', ->
202
227
 
203
- it 'reveals the given element', ->
228
+ it 'reveals the given element', asyncSpec (next) ->
204
229
  $viewport = affix('div').css
205
230
  'position': 'absolute'
206
231
  'top': '50px'
@@ -227,48 +252,56 @@ describe 'up.layout', ->
227
252
  # See that the view only scrolls down as little as possible
228
253
  # in order to reveal the element
229
254
  up.reveal($elements[3], viewport: $viewport)
230
- # [0] 000..049
231
- # [1] 050..099
232
- # ------------
233
- # [2] 100..149
234
- # [3] 150..199
235
- # ------------
236
- # [4] 200..249
237
- # [5] 250..299
238
- expect($viewport.scrollTop()).toBe(100)
239
255
 
240
- # See that the view doesn't move if the element
241
- # is already revealed
242
- up.reveal($elements[2], viewport: $viewport)
243
- expect($viewport.scrollTop()).toBe(100)
244
-
245
- # See that the view scrolls as far down as it cans
246
- # to show the bottom element
247
- up.reveal($elements[5], viewport: $viewport)
248
- # [0] 000..049
249
- # [1] 050..099
250
- # [2] 100..149
251
- # [3] 150..199
252
- # ------------
253
- # [4] 200..249
254
- # [5] 250..299
255
- # ------------
256
- expect($viewport.scrollTop()).toBe(200)
257
-
258
- # See that the view only scrolls up as little as possible
259
- # in order to reveal the element
260
- up.reveal($elements[1], viewport: $viewport)
261
- # [0] 000..049
262
- # ------------
263
- # [1] 050..099
264
- # [2] 100..149
265
- # ------------
266
- # [3] 150..199
267
- # [4] 200..249
268
- # [5] 250..299
269
- expect($viewport.scrollTop()).toBe(50)
270
-
271
- it 'only reveals the top number of pixels defined in config.substance', ->
256
+ next =>
257
+ # [0] 000..049
258
+ # [1] 050..099
259
+ # ------------
260
+ # [2] 100..149
261
+ # [3] 150..199
262
+ # ------------
263
+ # [4] 200..249
264
+ # [5] 250..299
265
+ expect($viewport.scrollTop()).toBe(100)
266
+
267
+ # See that the view doesn't move if the element
268
+ # is already revealed
269
+ up.reveal($elements[2], viewport: $viewport)
270
+
271
+ next =>
272
+ expect($viewport.scrollTop()).toBe(100)
273
+
274
+ # See that the view scrolls as far down as it cans
275
+ # to show the bottom element
276
+ up.reveal($elements[5], viewport: $viewport)
277
+
278
+ next =>
279
+ # [0] 000..049
280
+ # [1] 050..099
281
+ # [2] 100..149
282
+ # [3] 150..199
283
+ # ------------
284
+ # [4] 200..249
285
+ # [5] 250..299
286
+ # ------------
287
+ expect($viewport.scrollTop()).toBe(200)
288
+
289
+ up.reveal($elements[1], viewport: $viewport)
290
+
291
+ next =>
292
+ # See that the view only scrolls up as little as possible
293
+ # in order to reveal the element
294
+ # [0] 000..049
295
+ # ------------
296
+ # [1] 050..099
297
+ # [2] 100..149
298
+ # ------------
299
+ # [3] 150..199
300
+ # [4] 200..249
301
+ # [5] 250..299
302
+ expect($viewport.scrollTop()).toBe(50)
303
+
304
+ it 'only reveals the top number of pixels defined in config.substance', asyncSpec (next) ->
272
305
 
273
306
  up.layout.config.substance = 20
274
307
 
@@ -298,55 +331,67 @@ describe 'up.layout', ->
298
331
  # See that the view only scrolls down as little as possible
299
332
  # in order to reveal the first 20 rows of the element
300
333
  up.reveal($elements[3], viewport: $viewport)
301
- # Viewing 70 to 169
302
- expect($viewport.scrollTop()).toBe(50 + 20)
303
-
304
- # See that the view doesn't move if the element
305
- # is already revealed
306
- up.reveal($elements[2], viewport: $viewport)
307
- expect($viewport.scrollTop()).toBe(50 + 20)
308
-
309
- # See that the view scrolls as far down as it cans
310
- # to show the first 20 rows of the bottom element
311
- up.reveal($elements[5], viewport: $viewport)
312
- # Viewing 170 to 269
313
- expect($viewport.scrollTop()).toBe(150 + 20)
314
-
315
- # See that the view only scrolls up as little as possible
316
- # in order to reveal the first 20 rows element
317
- up.reveal($elements[2], viewport: $viewport)
318
- # Viewing 100 to 199
319
- expect($viewport.scrollTop()).toBe(100)
334
+
335
+ next =>
336
+ # Viewing 70 to 169
337
+ expect($viewport.scrollTop()).toBe(50 + 20)
338
+
339
+ # See that the view doesn't move if the element
340
+ # is already revealed
341
+ up.reveal($elements[2], viewport: $viewport)
342
+
343
+ next =>
344
+ expect($viewport.scrollTop()).toBe(50 + 20)
345
+
346
+ # See that the view scrolls as far down as it cans
347
+ # to show the first 20 rows of the bottom element
348
+ up.reveal($elements[5], viewport: $viewport)
349
+
350
+ next =>
351
+ # Viewing 170 to 269
352
+ expect($viewport.scrollTop()).toBe(150 + 20)
353
+
354
+ # See that the view only scrolls up as little as possible
355
+ # in order to reveal the first 20 rows element
356
+ up.reveal($elements[2], viewport: $viewport)
357
+
358
+ next =>
359
+ # Viewing 100 to 199
360
+ expect($viewport.scrollTop()).toBe(100)
320
361
 
321
362
  describe 'revealHash', ->
322
363
 
323
- it 'reveals an element with an ID matching the hash in the location', ->
364
+ it 'reveals an element with an ID matching the hash in the location', asyncSpec (next) ->
324
365
  revealSpy = up.layout.knife.mock('reveal')
325
366
  $match = affix('div#hash')
326
367
  location.hash = '#hash'
327
368
  up.layout.revealHash()
328
- expect(revealSpy).toHaveBeenCalledWith($match)
369
+ next => expect(revealSpy).toHaveBeenCalledWith($match)
329
370
 
330
- it 'reveals a named anchor matching the hash in the location', ->
371
+ it 'reveals a named anchor matching the hash in the location', asyncSpec (next) ->
331
372
  revealSpy = up.layout.knife.mock('reveal')
332
373
  $match = affix('a[name="hash"]')
333
374
  location.hash = '#hash'
334
375
  up.layout.revealHash()
335
- expect(revealSpy).toHaveBeenCalledWith($match)
376
+ next => expect(revealSpy).toHaveBeenCalledWith($match)
336
377
 
337
- it 'does nothing and returns a rejected promise if no element or anchor matches the hash in the location', ->
378
+ it 'does nothing and returns a fulfilled promise if no element or anchor matches the hash in the location', (done) ->
338
379
  revealSpy = up.layout.knife.mock('reveal')
339
380
  location.hash = '#hash'
340
381
  promise = up.layout.revealHash()
341
382
  expect(revealSpy).not.toHaveBeenCalled()
342
- expect(promise.state()).toEqual('rejected')
383
+ promiseState(promise).then (result) ->
384
+ expect(result.state).toEqual('fulfilled')
385
+ done()
343
386
 
344
- it 'does nothing and returns a resolved promise if the location has no hash', ->
387
+ it 'does nothing and returns a fulfilled promise if the location has no hash', (done) ->
345
388
  revealSpy = up.layout.knife.mock('reveal')
346
389
  location.hash = ''
347
390
  promise = up.layout.revealHash()
348
391
  expect(revealSpy).not.toHaveBeenCalled()
349
- expect(promise.state()).toEqual('resolved')
392
+ promiseState(promise).then (result) ->
393
+ expect(result.state).toEqual('fulfilled')
394
+ done()
350
395
 
351
396
  describe 'up.layout.viewportsWithin', ->
352
397
 
@@ -379,6 +424,29 @@ describe 'up.layout', ->
379
424
  lookup = -> up.layout.viewportOf($element)
380
425
  expect(lookup).toThrowError(/Could not find viewport/i)
381
426
 
427
+ describe 'up.layout.restoreScroll', ->
428
+
429
+ it "restores a viewport's previously saved scroll position", (done) ->
430
+ $viewport = affix('#viewport[up-viewport]').css(height: '100px', overflowY: 'scroll')
431
+ $content = $viewport.affix('.content').css(height: '1000px')
432
+ up.hello($viewport)
433
+ $viewport.scrollTop(50)
434
+ up.layout.saveScroll()
435
+ $viewport.scrollTop(70)
436
+
437
+ up.layout.restoreScroll().then ->
438
+ expect($viewport.scrollTop()).toEqual(50)
439
+ done()
440
+
441
+ it "scrolls a viewport to the top (and does not crash) if no previous scroll position is known", (done) ->
442
+ $viewport = affix('#viewport[up-viewport]').css(height: '100px', overflowY: 'scroll')
443
+ $content = $viewport.affix('.content').css(height: '1000px')
444
+ $viewport.scrollTop(70)
445
+
446
+ up.layout.restoreScroll().then ->
447
+ expect($viewport.scrollTop()).toEqual(0)
448
+ done()
449
+
382
450
  describe 'up.scroll', ->
383
451
 
384
452
  it 'should have tests'