UNPKG

11 kBJavaScriptView Raw
1var R = require('ramda');
2var assert = require('assert');
3var equalsInvoker = require('./utils').equalsInvoker;
4var types = require('./types')(equalsInvoker);
5var Future = require('../src/Future');
6
7Future.prototype.equals = function(b) {
8 this.fork(function(e1) {
9 b.fork(function(e2) {
10 assert.equal(e1, e2);
11 }, function() {
12 assert.fail(null, e1, 'Futures not equal: f1 failed, f2 did not', '===');
13 });
14 }, function(v1) {
15 b.fork(function() {
16 assert.fail(null, v1, 'Futures not equal: f1 succeeded, f2 did not', '===');
17 }, function(v2) {
18 assert.equal(v1, v2);
19 });
20 });
21 return true;
22};
23
24describe('Future', function() {
25
26 it('should equal another future', function() {
27 var f1 = Future.of(2);
28 var f2 = Future.of(2);
29 assert.equal(true, f1.equals(f2));
30 });
31
32 it('is a Functor', function() {
33 var fTest = types.functor;
34 var f = Future.of(2);
35 assert.equal(true, fTest.iface(f));
36 assert.equal(true, fTest.id(f));
37 assert.equal(true, fTest.compose(f, R.multiply(2), R.add(3)));
38 });
39
40 it('is an Apply', function() {
41 var aTest = types.apply;
42 var appA = Future.of(R.multiply(10));
43 var appU = Future.of(R.add(5));
44 var appV = Future.of(10);
45 assert.equal(true, aTest.iface(appA));
46 assert.equal(true, aTest.compose(appA, appU, appV));
47 });
48
49 it('is an Applicative', function() {
50 var aTest = types.applicative;
51 var app1 = Future.of(101);
52 var app2 = Future.of(-123);
53 var appF = Future.of(R.multiply(3));
54
55 assert.equal(true, aTest.iface(app1));
56 assert.equal(true, aTest.id(app1, app2));
57 assert.equal(true, aTest.homomorphic(app1, R.add(3), 46));
58 assert.equal(true, aTest.interchange(app1, appF, 17));
59 });
60
61 it('is a Chain', function() {
62 var cTest = types.chain;
63 var f = Future.of(2);
64 var f1 = function(x) {return Future.of((3 * x));};
65 var f2 = function(x) {return Future.of((5 + x));};
66
67 assert.equal(true, cTest.iface(f));
68 assert.equal(true, cTest.associative(f, f1, f2));
69 });
70
71 it('is a Monad', function() {
72 var mTest = types.monad;
73 var f = Future.of(null);
74 assert.equal(true, mTest.iface(f));
75 });
76
77 it('.map should work according to the functor specification', function() {
78 var result = Future.of(1).map(R.inc);
79 assert.equal(true, Future.of(2).equals(result));
80 });
81
82 it('.chain should work according to the chainable specification', function() {
83 var incInTheFuture = function(val) {
84 return Future.of(R.inc(val));
85 };
86 var result = Future.of(1).chain(incInTheFuture);
87 assert.equal(true, Future.of(2).equals(result));
88 });
89
90 describe('chainReject', function() {
91 it('.chainReject should work like chain but off reject case', function() {
92 var f1 = Future.reject(2);
93 var f2 = function(val){ return Future.of(val + 3);};
94 assert.equal(true, Future.of(5).equals(f1.chainReject(f2)));
95 });
96 });
97
98 describe('#ap', function() {
99 /*jshint browser:true */
100 var add = R.add;
101 function delayError(delay, err) {
102 /*jshint unused:false */
103 return new Future(function(reject, resolve) {
104 setTimeout(reject, delay, err);
105 });
106 }
107
108 function delayValue(delay, value) {
109 return new Future(function(reject, resolve) {
110 setTimeout(resolve, delay, value);
111 });
112 }
113
114 function assertCbVal(done, expectedVal) {
115 return function(val) {
116 assert.equal(expectedVal, val);
117 done();
118 };
119 }
120
121 it('applies its function to the passed in future', function() {
122 var f1 = Future.of(add(1));
123 var result = f1.ap(Future.of(2));
124 assert.equal(true, Future.of(3).equals(result));
125 });
126
127 it('does the apply in parallel', function(done) {
128 this.timeout(25);
129 var f1 = delayValue(15, 1);
130 var f2 = delayValue(15, 2);
131 f1.map(add).ap(f2).fork(assert.fail, assertCbVal(done, 3));
132 });
133
134 it('can handle itself being resolved first', function(done) {
135 var f1 = delayValue(1, 1);
136 var f2 = delayValue(15, 2);
137 f1.map(add).ap(f2).fork(assert.fail, assertCbVal(done, 3));
138 });
139
140 it('can handle the input future being resolved first', function(done) {
141 var f1 = delayValue(15, 1);
142 var f2 = delayValue(1, 2);
143 f1.map(add).ap(f2).fork(assert.fail, assertCbVal(done, 3));
144 });
145
146 it('is rejected with the first error to occur - case 1', function(done) {
147 var f1 = delayError(10, 'firstError');
148 var f2 = delayError(20, 'secondError');
149 f1.map(add).ap(f2).fork(assertCbVal(done, 'firstError'));
150 });
151
152 it('is rejected with the first error to occur - case 2', function(done) {
153 var f1 = delayError(20, 'firstError');
154 var f2 = delayError(10, 'secondError');
155 f1.map(add).ap(f2).fork(assertCbVal(done, 'secondError'));
156 });
157
158 });
159
160 describe('reject', function() {
161
162 it('creates a rejected future with the given value', function() {
163 var f = Future.reject('foo');
164 var forked;
165 f.fork(function(err) {
166 forked = true;
167 assert.equal('foo', err);
168 });
169 assert.equal(true, forked);
170 });
171
172 });
173
174 describe('#bimap', function() {
175
176 it('maps the first function over the rejected value', function() {
177 var f = Future.reject('err');
178 var result = f.bimap(R.concat('map over '));
179 result.fork(function(e) {
180 assert.equal(e, 'map over err');
181 });
182 });
183
184 it('maps the second function over the resolved value', function() {
185 var f = Future.of(1);
186 var result = f.bimap(null, R.add(1));
187 result.fork(null, function(v) {
188 assert.equal(v, 2);
189 });
190 });
191
192 });
193
194 describe('#toString', function() {
195
196 it('returns the string representation of a Future', function() {
197 assert.strictEqual(
198 Future(function(reject, resolve) { void resolve; }).toString(),
199 'Future(function (reject, resolve) { void resolve; })'
200 );
201 });
202
203 });
204
205 describe('#fork', function() {
206
207 var result;
208 var futureOne = Future.of(1);
209 var throwError = function() {
210 throw new Error('Some error message');
211 };
212 var setErrorResult = function(e) {
213 result = e.message;
214 };
215 var delayValue = function(delay, value) {
216 return new Future(function(reject, resolve) {
217 setTimeout(resolve, delay, value);
218 });
219 };
220
221 beforeEach(function() {
222 result = null;
223 });
224
225 it('creates a rejected future if the resolve function throws an error', function() {
226 futureOne.fork(setErrorResult, throwError);
227 assert.equal('Some error message', result);
228 });
229
230 it('rejects the future if an error is thrown in a map function', function() {
231 futureOne.map(throwError).fork(setErrorResult);
232 assert.equal('Some error message', result);
233 });
234
235 it('rejects the future if an error is thrown in a chain function', function() {
236 futureOne.chain(throwError).fork(setErrorResult);
237 assert.equal('Some error message', result);
238 });
239
240 it('rejects the future if an error is thrown in a ap function', function() {
241 Future.of(throwError).ap(futureOne).fork(setErrorResult);
242 assert.equal('Some error message', result);
243 });
244
245 it('eventually rejects the future if an error is thrown in a chain function', function(done){
246 var throwEscapeError = function(){
247 throw new Error('This error should be caught');
248 };
249 delayValue(15, 1).chain(throwEscapeError).fork(
250 function(err){
251 assert.equal('This error should be caught', err.message);
252 done();
253 },
254 function(){
255 done(new Error('The future resolved'));
256 }
257 );
258 });
259
260 });
261
262 describe('#cache', function() {
263 var cached;
264 var throwIfCalledTwice;
265
266 beforeEach(function() {
267 throwIfCalledTwice = (function() {
268 var count = 0;
269 return function(val) {
270 if (++count > 1) {
271 throw new Error('Was called twice');
272 }
273 return val;
274 };
275 }());
276 });
277
278 describe('resolve cases', function() {
279
280 beforeEach(function() {
281 cached = Future.cache(Future.of(1).map(throwIfCalledTwice));
282 });
283
284 it('can be forked with a resolved value', function(done) {
285 cached.fork(done, function(v) {
286 assert.equal(1, v);
287 done();
288 });
289 });
290
291 it('passes on the same value to the cached future', function(done) {
292 cached.fork(done, function() {
293 cached.fork(done, function(v) {
294 assert.equal(1, v);
295 done();
296 });
297 });
298 });
299
300 });
301
302 describe('reject cases', function() {
303
304 var throwError = function() {
305 throw new Error('SomeError');
306 };
307
308 beforeEach(function() {
309 cached = Future.cache(Future.of(1).map(throwIfCalledTwice).map(throwError));
310 });
311
312 it('can be forked with a rejected value', function() {
313 var result;
314 cached.fork(function(err) {
315 result = err.message;
316 });
317 assert.equal('SomeError', result);
318 });
319
320 it('does not call the underlying fork twice', function() {
321 var result;
322 cached.fork(function() {
323 cached.fork(function(err) {
324 result = err.message;
325 });
326 });
327 assert.equal('SomeError', result);
328 });
329
330 });
331
332 describe('pending cases', function() {
333
334 it('calls all fork resolve functions when the cached future is resolved', function(done) {
335 var delayed = new Future(function(reject, resolve) {
336 setTimeout(resolve, 5, 'resolvedValue');
337 });
338 var cached = Future.cache(delayed.map(throwIfCalledTwice));
339 var result1;
340 var result2;
341 function assertBoth() {
342 if (result1 !== undefined && result2 !== undefined) {
343 assert.equal('resolvedValue', result1);
344 assert.equal('resolvedValue', result2);
345 done();
346 }
347 }
348 cached.fork(done, function(v) {
349 result1 = v;
350 assertBoth();
351 });
352 cached.fork(done, function(v) {
353 result2 = v;
354 assertBoth();
355 });
356 });
357
358 it('calls all fork reject fnctions when the cached future is rejected', function(done) {
359 var delayed = new Future(function(reject) {
360 setTimeout(reject, 5, 'rejectedValue');
361 });
362 var cached = Future.cache(delayed.bimap(throwIfCalledTwice, R.identity));
363 var result1;
364 var result2;
365 function assertBoth() {
366 if (result1 !== undefined && result2 !== undefined) {
367 assert.equal('rejectedValue', result1);
368 assert.equal('rejectedValue', result2);
369 done();
370 }
371 }
372 cached.fork(function(e) {
373 result1 = e;
374 assertBoth();
375 });
376 cached.fork(function(e) {
377 result2 = e;
378 assertBoth();
379 });
380
381 });
382
383 });
384
385 });
386
387});
388