9 describe('Class.Thenable', function(){
11 var Thenable = Class.Thenable;
13 var Thing = new Class({
21 resolved: Class.Thenable.resolve,
23 rejected: Class.Thenable.reject,
26 var thenable = new Class.Thenable();
30 resolve: thenable.resolve.bind(thenable),
31 reject: thenable.reject.bind(thenable)
37 function asyncExpectations(done, fn){
41 function notFinished(){
52 if (finished) return done(error);
56 // Tests below are adapted versions of the tests in domenic/promises-unwrapping:
57 // https://github.com/domenic/promises-unwrapping/tree/master/reference-implementation/test
58 it('should call its fulfillment handler when fulfilled', function(done){
59 var expectations = asyncExpectations(done, function(){
60 expect(onFulfilled.called).to.equal(true);
61 expect(onRejected.called).to.equal(false);
62 expect(onFulfilled.args[0][0]).to.equal(5);
65 var onFulfilled = sinon.spy(expectations);
66 var onRejected = sinon.spy(expectations);
68 Thenable.resolve(5).then(onFulfilled, onRejected);
71 it('should call its rejection handler when rejected', function(done){
72 var expectations = asyncExpectations(done, function(){
73 expect(onRejected.called).to.equal(true);
74 expect(onRejected.args[0][0]).to.equal(12);
77 var onRejected = sinon.spy(expectations);
79 Thenable.reject(12)['catch'](onRejected);
82 describe('implemented', function(){
84 it('should call its fulfillment handler when fulfilled', function(done){
85 var expectations = asyncExpectations(done, function(){
86 expect(onFulfilled.called).to.equal(true);
87 expect(onRejected.called).to.equal(false);
88 expect(onFulfilled.args[0][0]).to.equal(4);
91 var instance = new Thing();
92 var onFulfilled = sinon.spy(expectations);
93 var onRejected = sinon.spy(expectations);
95 instance.then(onFulfilled, onRejected);
99 it('should call its rejection handler when rejected', function(done){
100 var expectations = asyncExpectations(done, function(){
101 expect(onRejected.called).to.equal(true);
102 expect(onRejected.args[0][0]).to.equal(41);
105 var instance = new Thing();
106 var onRejected = sinon.spy(expectations);
109 instance['catch'](onRejected);
114 it('should refuse to be resolved with itself', function(done){
115 var expectations = asyncExpectations(done, function(){
116 expect(onFulfilled.called).to.equal(false);
117 expect(onRejected.called).to.equal(true);
118 expect(onRejected.args[0][0] instanceof TypeError).to.equal(true);
121 var onFulfilled = sinon.spy(expectations);
122 var onRejected = sinon.spy(expectations);
124 var thenable = new Thenable();
125 thenable.resolve(thenable).then(onFulfilled, onRejected);
128 it('should call handlers in the order they are queued, when added before resolution', function(done){
129 var expectations = asyncExpectations(done, function(notFinished){
130 if (onFulfilled.callCount == 2){
131 expect(calls[0]).to.equal(2);
132 expect(calls[1]).to.equal(1);
138 var onFulfilled = sinon.spy(expectations);
141 var t1 = new Thenable();
142 var t2 = new Thenable();
146 }).then(onFulfilled);
148 t2['catch'](function(){
150 }).then(onFulfilled);
156 it('should call handlers in the order they are queued, when added after resolution', function(done){
157 var expectations = asyncExpectations(done, function(notFinished){
158 if (onFulfilled.callCount == 2){
159 expect(calls[0]).to.equal(1);
160 expect(calls[1]).to.equal(2);
166 var onFulfilled = sinon.spy(expectations);
169 var t1 = new Thenable();
170 var t2 = new Thenable();
177 }).then(onFulfilled);
179 t2['catch'](function(){
181 }).then(onFulfilled);
184 it('should call handlers in the order they are queued, when added asynchronously after resolution', function(done){
185 var expectations = asyncExpectations(done, function(notFinished){
186 if (onFulfilled.callCount == 2){
187 expect(calls[0]).to.equal(1);
188 expect(calls[1]).to.equal(2);
194 var onFulfilled = sinon.spy(expectations);
197 var t1 = new Thenable();
198 var t2 = new Thenable();
203 setTimeout(function(){
206 }).then(onFulfilled);
208 t2['catch'](function(){
210 }).then(onFulfilled);
214 it('should resolve only once, even when resolved to a thenable that calls its handlers twice', function(done){
215 var expectations = asyncExpectations(done, function(){
216 expect(onFulfilled.callCount).to.equal(1);
217 expect(onFulfilled.args[0][0]).to.equal(1);
220 var onFulfilled = sinon.spy(expectations);
222 var evilThenable = Thenable.resolve();
223 evilThenable.then = function(f){
228 var resolvedToEvil = new Thenable();
229 resolvedToEvil.resolve(evilThenable);
230 resolvedToEvil.then(onFulfilled);
233 it('should correctly store the first resolved value if resolved to a thenable which calls back with different values each time', function(done){
234 var expectations = asyncExpectations(done, function(){
235 expect(onFulfilled.called).to.equal(true);
238 var onFulfilled = sinon.spy(expectations);
246 var t = Thenable.resolve(thenable);
248 t.then(function(value){
249 expect(value).to.equal(0);
251 t.then(function(value){
252 expect(value).to.equal(0);
254 t.then(function(value){
255 expect(value).to.equal(0);
263 it('should call then methods with a clean stack when it resolves to a thenable', function(){
264 var then = sinon.spy();
270 Thenable.resolve(thenable);
272 expect(then.called).to.equal(false);
275 it('should call then methods with a clean stack when it resolves to an (evil) Thenable', function(){
276 var then = sinon.spy();
278 var evilThenable = Thenable.resolve();
279 evilThenable.then = then;
281 Thenable.resolve(evilThenable);
282 expect(then.called).to.equal(false);
285 it('should reset correctly', function(){
286 var thenable = new Thenable.resolve(3)
287 expect(thenable.getThenableState()).to.equal('fulfilled');
289 thenable.resetThenable();
290 expect(thenable.getThenableState()).to.equal('pending');
293 it('should reject before resetting, if still pending', function(done){
294 var expectations = asyncExpectations(done, function(){
295 expect(onRejected.called).to.equal(true);
296 expect(onRejected.args[0][0]).to.equal(7);
297 expect(thenable.getThenableState()).to.equal('pending');
300 var onRejected = sinon.spy(expectations);
302 var thenable = new Thenable();
303 expect(thenable.getThenableState()).to.equal('pending');
305 thenable.then(null, onRejected);
307 thenable.resetThenable(7);