-
Notifications
You must be signed in to change notification settings - Fork 29
/
deferred_test.coffee
477 lines (409 loc) · 14.6 KB
/
deferred_test.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
deferred = require './deferred'
assert = require 'assert'
_ = require 'underscore'
expectedMethods = ['done', 'fail', 'progress', 'always', 'state', 'then', 'pipe']
assertHasPromiseApi = (promise) -> assert _.has(promise, method) for method in expectedMethods
assertIsPromise = (promise) -> assertHasPromiseApi promise
describe 'deferred', ->
it 'should create and return a deferred object', ->
def = new deferred.Deferred()
assert.equal def.state(), "pending"
it 'should maintain a resolved state', ->
def = new deferred.Deferred()
assert.equal def.state(), "pending"
def.resolve()
assert.equal def.state(), "resolved"
def.resolve()
assert.equal def.state(), "resolved"
def.reject()
assert.equal def.state(), "resolved"
it 'should maintain a rejected state', ->
def = new deferred.Deferred()
assert.equal def.state(), "pending"
def.reject()
assert.equal def.state(), "rejected"
def.reject()
assert.equal def.state(), "rejected"
def.resolve()
assert.equal def.state(), "rejected"
it 'should maintain a pending state', ->
def = new deferred.Deferred()
assert.equal def.state(), "pending"
def.notify()
assert.equal def.state(), "pending"
it 'should call all the done callbacks', (done) ->
def = new deferred.Deferred()
callback = _.after 8, done
def.done(callback).done([callback, callback])
def.resolve()
def.done callback, callback
def.reject()
def.done callback, [callback, callback]
def.fail callback, callback
it 'should scope done callbacks when using resolveWith', (done) ->
callback = _.after 2, done
def = new deferred.Deferred()
finishHolder = {finisher: callback}
finish = (arg1) ->
assert.equal 42, arg1
@finisher()
def.done finish
def.always -> callback()
def.resolveWith(finishHolder, [42])
assert.equal def.state(), 'resolved'
it 'should return a deferred for empty calls', ->
assertIsPromise new deferred.Deferred().resolveWith()
it 'should not mangle arrays', ->
def = new deferred.Deferred()
def.promise().done (arg1, arg2) ->
assert.equal arg1, 1
assert.equal arg2, 2
def.resolveWith(def, [1,2])
it 'should scope fail callbacks when using rejectWith', (done) ->
callback = _.after 2, done
def = new deferred.Deferred()
finishHolder = {finisher: callback}
finish = (arg1) ->
assert.equal 42, arg1
@finisher()
def.fail finish
def.always -> callback()
def.rejectWith(finishHolder, [42])
assert.equal def.state(), 'rejected'
it 'should call all the fail callbacks', (done) ->
def = new deferred.Deferred()
callback = _.after 8, done
def.fail(callback).fail([callback, callback])
def.reject()
def.fail callback, callback
def.resolve()
def.fail callback, [callback, callback]
def.done callback
it 'should scope progress callbacks when using notifyWith', (done) ->
callback = _.after 2, done
def = new deferred.Deferred()
finishHolder = {finisher: callback}
finish = (arg1) ->
assert.equal 42, arg1
@finisher()
def.progress finish
def.progress -> callback()
def.notifyWith(finishHolder, [42])
assert.equal def.state(), 'pending'
it 'should call all the progress callbacks each notification', (done) ->
def = new deferred.Deferred()
callback = _.after 9, done
def.progress(callback).progress([callback, callback])
def.notify()
def.progress(callback).progress([callback, callback])
def.notify()
it 'should call progress callbacks with updated arguments on each notification', (done) ->
def = new deferred.Deferred()
i = 0
callback = (arg1) ->
assert.equal arg1, i
if arg1 is 2
done()
def.progress(callback)
def.notify i
i++
def.notify i
i++
def.notify i
it 'should run notify callbacks and then accept resolution', (done) ->
def = new deferred.Deferred();
callback = _.after 2, done
def.progress(callback)
def.notify()
assert.equal def.state(), 'pending'
def.done(callback)
def.resolve()
it 'should run notify callbacks and then accept rejection', (done) ->
def = new deferred.Deferred();
callback = _.after 2, done
def.progress(callback)
def.notify()
assert.equal def.state(), 'pending'
def.fail(callback)
def.reject()
it 'should not run notify callbacks after being resolved', (done) ->
def = new deferred.Deferred();
callback = _.after 2, done
def.progress(callback)
def.notify()
def.done(callback)
def.resolve()
def.notify()
it 'should run additional progress callbacks imediately after being resolved', (done) ->
def = new deferred.Deferred();
callback = _.after 3, done
def.progress(callback)
def.notify()
def.done(callback)
def.resolve()
assert.equal def.state(), 'resolved'
def.progress(callback)
it 'should run additional progress callbacks imediately after being rejected', (done) ->
def = new deferred.Deferred();
callback = _.after 3, done
def.progress(callback)
def.notify()
def.fail(callback)
def.reject()
assert.equal def.state(), 'rejected'
def.progress(callback)
it 'should run additional progress callbacks with the last sent notify arguments', (done) ->
def = new deferred.Deferred();
callback = _.after 3, (arg1) ->
if arg1 is 2
done()
def.progress(callback)
def.notify 1
def.notify 2
def.resolve 5
def.progress(callback)
it 'should call all the always callbacks on resolution', (done) ->
def = new deferred.Deferred()
callback = _.after 8, done
def.always(callback).always([callback, callback])
def.resolve()
def.always callback, callback
def.always callback, [callback, callback]
def.fail callback
it 'should call the always callbacks on rejection', (done) ->
def = new deferred.Deferred()
def.always done
def.reject()
def.done done
it 'should call callbacks with arguments', (done) ->
finish = _.after 8, done
callback = (arg1, arg2) ->
if arg1 is 42 and arg2 is 24
finish()
new deferred.Deferred().always(callback).resolve(42, 24).always(callback)
new deferred.Deferred().always(callback).reject(42, 24).always(callback)
new deferred.Deferred().done(callback).resolve(42, 24).done(callback)
new deferred.Deferred().fail(callback).reject(42, 24).fail(callback)
it 'should provide a when method', (done) ->
callback = _.after 4, -> done()
def1 = new deferred.Deferred().done callback
def2 = new deferred.Deferred().done callback
def3 = new deferred.Deferred().done callback
all = deferred.when(def1, def2, def3).done callback
def1.resolve()
def2.resolve()
def3.resolve()
describe 'pipe', ->
it 'should pipe on resolution', (done) ->
finisher = (value) -> if value is 10 then done()
def = new deferred.Deferred()
filtered = def.pipe (value) -> value * 2
def.resolve 5
filtered.done finisher
it 'should pipe on rejection', (done) ->
finisher = (value) -> if value is 6 then done()
def = new deferred.Deferred()
filtered = def.pipe null, (value) -> value * 3
def.reject 2
filtered.fail finisher
it 'should pipe on progress', (done) ->
finisher = (value) -> if value is 6 then done()
def = new deferred.Deferred()
filtered = def.pipe null, null, (value) -> value * 3
filtered.progress finisher
def.notify 2
it 'should pipe with arrays intact', (done) ->
finisher = (value) ->
if value.length is [1,2,3].length then done()
def = new deferred.Deferred()
filtered = def.pipe null, (value) ->
value.push(3)
value
def.reject([1,2])
filtered.fail finisher
it 'should pass through for null filters for done', (done) ->
finisher = (value) -> if value is 5 then done()
def = new deferred.Deferred()
filtered = def.pipe(null, null)
def.resolve 5
filtered.done finisher
it 'should pass through for null filters for fail', (done) ->
finisher = (value) -> if value is 5 then done()
def = new deferred.Deferred()
filtered = def.pipe(null, null)
def.reject 5
filtered.fail finisher
it 'should pass through for null filters for notify', (done) ->
finisher = (value) -> if value is 5 then done()
def = new deferred.Deferred()
filtered = def.pipe(null, null, null)
filtered.progress finisher
def.notify 5
it 'should accept promises from filters and call them later with arguments', (done) ->
def = deferred.Deferred()
filter = (result) ->
assert.equal result, 'r1'
def2 = deferred.Deferred()
setTimeout (-> def2.resolve('r2')), 100
def2
def.then(filter).done (result) ->
assert.equal result, 'r2'
done() if result is 'r2'
def.resolve('r1')
null
it 'should allow changing the state', (done) ->
def = deferred.Deferred()
def.pipe((result) ->
return deferred.Deferred().reject('failure').promise()
).fail((msg) ->
assert.equal msg, 'failure'
done() if msg is 'failure'
)
def.resolve('r1')
describe 'then', ->
it 'should alias pipe', ->
def = new deferred.Deferred()
assert.equal def.then, def.pipe
describe 'promises', ->
it 'should provide a promise that has a restricted API', (done) ->
def = new deferred.Deferred()
promise = def.promise()
assertIsPromise promise
callback = _.after 7, done
promise.always(callback).always(callback).progress(callback).fail(callback).done(callback).fail(callback)
assertIsPromise promise.progress callback
assertIsPromise promise.done callback
assertIsPromise promise.fail callback
assertIsPromise promise.always callback
assert.equal "pending", promise.state()
def.notify()
assert.equal "pending", promise.state()
def.resolve()
assert.equal "resolved", promise.state()
it 'should create a promise out of a given object', ->
candidate = {id: 42}
def = new deferred.Deferred()
promise = def.promise(candidate)
assert.equal candidate, promise
assertHasPromiseApi candidate
it 'should soak up extraneous promises', ->
def = new deferred.Deferred()
promise = def.promise().promise()
assertIsPromise promise
promise.done (arg) -> assert.equal arg, 42
def.resolve(42)
describe 'when', ->
it 'should return a promise', ->
assertIsPromise deferred.when new deferred.Deferred()
it 'should resolve when all deps have succeeded', ->
d1 = new deferred.Deferred()
d2 = new deferred.Deferred()
after_all = deferred.when(d1, d2)
d1.resolve()
assert.equal after_all.state(), 'pending'
d2.resolve()
assert.equal after_all.state(), 'resolved'
it 'should reject when there are some failures', ->
d1 = new deferred.Deferred()
d2 = new deferred.Deferred()
after_all = deferred.when(d1, d2)
d1.resolve()
assert.equal after_all.state(), 'pending'
d2.reject()
assert.equal after_all.state(), 'rejected'
it 'should pass on reject arguments', (done) ->
d1 = new deferred.Deferred()
d2 = new deferred.Deferred()
after_all = deferred.when(d1, d2)
after_all.fail (arg1) -> done() if arg1 is 42
d1.resolve()
d2.reject 42
it 'should pass on resolve arguments as is when used with a single deferred', (done) ->
d1 = new deferred.Deferred()
after_all = deferred.when(d1)
assert.equal d1, after_all
after_all.done (arg1) -> done() if arg1 is 42
d1.resolve(42)
it 'should special case single or no arguments when using multiple deferreds', (done) ->
d1 = new deferred.Deferred()
d2 = new deferred.Deferred()
d3 = new deferred.Deferred()
after_all = deferred.when(d1, d2, d3)
after_all.done (arg1, arg2, arg3) ->
assert.equal arg1, 42
assert.equal arg2, undefined
assert.deepEqual arg3, ['abc', 123]
done()
d2.resolve()
d3.resolve('abc', 123)
d1.resolve(42)
it 'should handle non promise arguments', ->
deferred.when(1, 2, 42).done((arg1, arg2, arg3) ->
assert.equal arg1, 1
assert.equal arg2, 2
assert.equal arg3, 42
)
it 'should handle zero arguments', (done) ->
deferred.when().done(done)
it 'should pass objects through', ->
value = {}
deferred.when(value).done (deferredValue) ->
assert.strictEqual value, deferredValue
it 'should pass arrays through', ->
value = []
deferred.when(value).done (deferredValue) ->
assert.strictEqual value, deferredValue
describe 'installation into a jQuery compatible library', ->
exampleArgs = [42, 24]
it 'should install .Deferred', ->
zepto = {}
deferred.installInto(zepto)
assertHasPromiseApi zepto.Deferred()
it 'should install .when', ->
zepto = {}
deferred.installInto(zepto)
assert.equal zepto.when.toString(), deferred.when.toString()
it 'should wrap .ajax()', (done) ->
zepto = {}
zepto.ajax = (options) -> done()
deferred.installInto zepto
assertIsPromise zepto.ajax()
it 'should resolve on success', (done) ->
callback = _.after 3, done
zepto = {}
zepto.ajax = (options) -> options.success(exampleArgs...)
deferred.installInto zepto
success = (args...) -> if args.length is exampleArgs.length then callback()
promise = zepto.ajax({
success: success
})
promise.done success
promise.always success
promise.fail -> fail()
it 'should provide an abort mechanism', (done) ->
zepto = {}
zepto.ajax = (options) -> {
abort: -> done()
}
deferred.installInto zepto
promise = zepto.ajax()
promise.abort()
it 'should reject on failure', (done) ->
callback = _.after 3, done
zepto = {}
zepto.ajax = (options) -> options.error(exampleArgs...)
deferred.installInto zepto
error = (args...) -> if args.length is exampleArgs.length then callback()
promise = zepto.ajax({
error: error
})
promise.fail error
promise.always error
promise.done -> fail()
it 'should work when no ajax callbacks are provided', (done) ->
zepto = {}
zepto.ajax = (options) -> options.success()
deferred.installInto zepto
zepto.ajax({
success: null
}).done(done)