From 1a01a1f682f981259fced48a3d8b9aa2716b8856 Mon Sep 17 00:00:00 2001 From: Oknesar Date: Mon, 12 Feb 2018 17:42:15 +0200 Subject: [PATCH 1/3] co-bluebird --- co-bluebird.js | 246 +++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 3 + 2 files changed, 249 insertions(+) create mode 100644 co-bluebird.js diff --git a/co-bluebird.js b/co-bluebird.js new file mode 100644 index 0000000..e13522d --- /dev/null +++ b/co-bluebird.js @@ -0,0 +1,246 @@ + +/** + * Swap default Promise + * look http://bluebirdjs.com/docs/why-bluebird.html + */ + +var Promise = require('bluebird') + +/** + * slice() reference. + */ + +var slice = Array.prototype.slice; + +/** + * Expose `co`. + */ + +module.exports = co['default'] = co.co = co; + +/** + * Wrap the given generator `fn` into a + * function that returns a promise. + * This is a separate function so that + * every `co()` call doesn't create a new, + * unnecessary closure. + * + * @param {GeneratorFunction} fn + * @return {Function} + * @api public + */ + +co.wrap = function (fn) { + createPromise.__generatorFunction__ = fn; + return createPromise; + function createPromise() { + return co.call(this, fn.apply(this, arguments)); + } +}; + +/** + * Execute the generator function or a generator + * and return a promise. + * + * @param {Function} fn + * @return {Promise} + * @api public + */ + +function co(gen) { + var ctx = this; + var args = slice.call(arguments, 1); + + // we wrap everything in a promise to avoid promise chaining, + // which leads to memory leak errors. + // see https://github.com/tj/co/issues/180 + return new Promise(function(resolve, reject) { + if (typeof gen === 'function') gen = gen.apply(ctx, args); + if (!gen || typeof gen.next !== 'function') return resolve(gen); + + onFulfilled(); + + /** + * @param {Mixed} res + * @return {Promise} + * @api private + */ + + function onFulfilled(res) { + var ret; + try { + ret = gen.next(res); + } catch (e) { + return reject(e); + } + next(ret); + return null; + } + + /** + * @param {Error} err + * @return {Promise} + * @api private + */ + + function onRejected(err) { + var ret; + try { + ret = gen.throw(err); + } catch (e) { + return reject(e); + } + next(ret); + } + + /** + * Get the next value in the generator, + * return a promise. + * + * @param {Object} ret + * @return {Promise} + * @api private + */ + + function next(ret) { + if (ret.done) return resolve(ret.value); + var value = toPromise.call(ctx, ret.value); + if (value && isPromise(value)) return value.then(onFulfilled, onRejected); + return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, ' + + 'but the following object was passed: "' + String(ret.value) + '"')); + } + }); +} + +/** + * Convert a `yield`ed value into a promise. + * + * @param {Mixed} obj + * @return {Promise} + * @api private + */ + +function toPromise(obj) { + if (!obj) return obj; + if (isPromise(obj)) return obj; + if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj); + if ('function' == typeof obj) return thunkToPromise.call(this, obj); + if (Array.isArray(obj)) return arrayToPromise.call(this, obj); + if (isObject(obj)) return objectToPromise.call(this, obj); + return obj; +} + +/** + * Convert a thunk to a promise. + * + * @param {Function} + * @return {Promise} + * @api private + */ + +function thunkToPromise(fn) { + var ctx = this; + return new Promise(function (resolve, reject) { + fn.call(ctx, function (err, res) { + if (err) return reject(err); + if (arguments.length > 2) res = slice.call(arguments, 1); + resolve(res); + }); + }); +} + +/** + * Convert an array of "yieldables" to a promise. + * Uses `Promise.all()` internally. + * + * @param {Array} obj + * @return {Promise} + * @api private + */ + +function arrayToPromise(obj) { + return Promise.all(obj.map(toPromise, this)); +} + +/** + * Convert an object of "yieldables" to a promise. + * Uses `Promise.all()` internally. + * + * @param {Object} obj + * @return {Promise} + * @api private + */ + +function objectToPromise(obj){ + var results = new obj.constructor(); + var keys = Object.keys(obj); + var promises = []; + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + var promise = toPromise.call(this, obj[key]); + if (promise && isPromise(promise)) defer(promise, key); + else results[key] = obj[key]; + } + return Promise.all(promises).then(function () { + return results; + }); + + function defer(promise, key) { + // predefine the key in the result + results[key] = undefined; + promises.push(promise.then(function (res) { + results[key] = res; + })); + } +} + +/** + * Check if `obj` is a promise. + * + * @param {Object} obj + * @return {Boolean} + * @api private + */ + +function isPromise(obj) { + return 'function' == typeof obj.then; +} + +/** + * Check if `obj` is a generator. + * + * @param {Mixed} obj + * @return {Boolean} + * @api private + */ + +function isGenerator(obj) { + return 'function' == typeof obj.next && 'function' == typeof obj.throw; +} + +/** + * Check if `obj` is a generator function. + * + * @param {Mixed} obj + * @return {Boolean} + * @api private + */ + +function isGeneratorFunction(obj) { + var constructor = obj.constructor; + if (!constructor) return false; + if ('GeneratorFunction' === constructor.name || 'GeneratorFunction' === constructor.displayName) return true; + return isGenerator(constructor.prototype); +} + +/** + * Check for plain object. + * + * @param {Mixed} val + * @return {Boolean} + * @api private + */ + +function isObject(val) { + return Object == val.constructor; +} diff --git a/package.json b/package.json index 28244ea..7a8501a 100644 --- a/package.json +++ b/package.json @@ -31,5 +31,8 @@ "engines": { "iojs": ">= 1.0.0", "node": ">= 0.12.0" + }, + "dependencies": { + "bluebird": "^3.5.1" } } From f4b0655a22e3d3594f8f4c130c82c1070cbea021 Mon Sep 17 00:00:00 2001 From: Oknesar Date: Mon, 12 Feb 2018 17:43:55 +0200 Subject: [PATCH 2/3] add co-bluebird.js to "files" --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 7a8501a..4e0a0a4 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "browserify": "browserify index.js -o ./co-browser.js -s co" }, "files": [ + "co-bluebird.js", "co-browser.js", "index.js" ], From 23a95e5a9e13e7abfd1c529cb38a4f3b7f839fc4 Mon Sep 17 00:00:00 2001 From: Oknesar Date: Mon, 12 Feb 2018 22:20:56 +0200 Subject: [PATCH 3/3] co-bluebird mocha tests --- package.json | 2 +- test/bluebird/arguments.js | 16 ++ test/bluebird/arrays.js | 35 ++++ test/bluebird/context.js | 16 ++ test/bluebird/generator-functions.js | 46 +++++ test/bluebird/generators.js | 46 +++++ test/bluebird/invalid.js | 18 ++ test/bluebird/objects.js | 86 ++++++++ test/bluebird/promises.js | 98 ++++++++++ test/bluebird/recursion.js | 42 ++++ test/bluebird/thunks.js | 281 +++++++++++++++++++++++++++ test/bluebird/wrap.js | 28 +++ 12 files changed, 713 insertions(+), 1 deletion(-) create mode 100644 test/bluebird/arguments.js create mode 100644 test/bluebird/arrays.js create mode 100644 test/bluebird/context.js create mode 100644 test/bluebird/generator-functions.js create mode 100644 test/bluebird/generators.js create mode 100644 test/bluebird/invalid.js create mode 100644 test/bluebird/objects.js create mode 100644 test/bluebird/promises.js create mode 100644 test/bluebird/recursion.js create mode 100644 test/bluebird/thunks.js create mode 100644 test/bluebird/wrap.js diff --git a/package.json b/package.json index 4e0a0a4..b86835d 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "mz": "^1.0.2" }, "scripts": { - "test": "mocha --harmony", + "test": "mocha --recursive --harmony", "test-cov": "node --harmony node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- --reporter dot", "test-travis": "node --harmony node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha --report lcovonly -- --reporter dot", "prepublish": "npm run browserify", diff --git a/test/bluebird/arguments.js b/test/bluebird/arguments.js new file mode 100644 index 0000000..33345d4 --- /dev/null +++ b/test/bluebird/arguments.js @@ -0,0 +1,16 @@ + +var assert = require('assert'); + +var co = require('../../co-bluebird'); + +describe('co(gen, args)', function () { + it('should pass the rest of the arguments', function () { + return co(function *(num, str, arr, obj, fun){ + assert(num === 42); + assert(str === 'forty-two'); + assert(arr[0] === 42); + assert(obj.value === 42); + assert(fun instanceof Function) + }, 42, 'forty-two', [42], { value: 42 }, function () {}); + }) +}) diff --git a/test/bluebird/arrays.js b/test/bluebird/arrays.js new file mode 100644 index 0000000..865cab0 --- /dev/null +++ b/test/bluebird/arrays.js @@ -0,0 +1,35 @@ + +var read = require('mz/fs').readFile; +var assert = require('assert'); + +var co = require('../../co-bluebird'); + +describe('co(* -> yield [])', function(){ + it('should aggregate several promises', function(){ + return co(function *(){ + var a = read('index.js', 'utf8'); + var b = read('LICENSE', 'utf8'); + var c = read('package.json', 'utf8'); + + var res = yield [a, b, c]; + assert.equal(3, res.length); + assert(~res[0].indexOf('exports')); + assert(~res[1].indexOf('MIT')); + assert(~res[2].indexOf('devDependencies')); + }); + }) + + it('should noop with no args', function(){ + return co(function *(){ + var res = yield []; + assert.equal(0, res.length); + }); + }) + + it('should support an array of generators', function(){ + return co(function*(){ + var val = yield [function*(){ return 1 }()] + assert.deepEqual(val, [1]) + }) + }) +}) diff --git a/test/bluebird/context.js b/test/bluebird/context.js new file mode 100644 index 0000000..8331744 --- /dev/null +++ b/test/bluebird/context.js @@ -0,0 +1,16 @@ + +var assert = require('assert'); + +var co = require('../../co-bluebird'); + +var ctx = { + some: 'thing' +}; + +describe('co.call(this)', function () { + it('should pass the context', function () { + return co.call(ctx, function *(){ + assert(ctx == this); + }); + }) +}) diff --git a/test/bluebird/generator-functions.js b/test/bluebird/generator-functions.js new file mode 100644 index 0000000..301a16e --- /dev/null +++ b/test/bluebird/generator-functions.js @@ -0,0 +1,46 @@ + +var assert = require('assert'); +var co = require('../../co-bluebird'); + +function sleep(ms) { + return function(done){ + setTimeout(done, ms); + } +} + +function *work() { + yield sleep(50); + return 'yay'; +} + +describe('co(fn*)', function(){ + describe('with a generator function', function(){ + it('should wrap with co()', function(){ + return co(function *(){ + var a = yield work; + var b = yield work; + var c = yield work; + + assert('yay' == a); + assert('yay' == b); + assert('yay' == c); + + var res = yield [work, work, work]; + assert.deepEqual(['yay', 'yay', 'yay'], res); + }); + }) + + it('should catch errors', function(){ + return co(function *(){ + yield function *(){ + throw new Error('boom'); + }; + }).then(function () { + throw new Error('wtf') + }, function (err) { + assert(err); + assert(err.message == 'boom'); + }); + }) + }) +}) diff --git a/test/bluebird/generators.js b/test/bluebird/generators.js new file mode 100644 index 0000000..51e5b28 --- /dev/null +++ b/test/bluebird/generators.js @@ -0,0 +1,46 @@ + +var assert = require('assert'); +var co = require('../../co-bluebird'); + +function sleep(ms) { + return function(done){ + setTimeout(done, ms); + } +} + +function *work() { + yield sleep(50); + return 'yay'; +} + +describe('co(*)', function(){ + describe('with a generator function', function(){ + it('should wrap with co()', function(){ + return co(function *(){ + var a = yield work; + var b = yield work; + var c = yield work; + + assert('yay' == a); + assert('yay' == b); + assert('yay' == c); + + var res = yield [work, work, work]; + assert.deepEqual(['yay', 'yay', 'yay'], res); + }); + }) + + it('should catch errors', function(){ + return co(function *(){ + yield function *(){ + throw new Error('boom'); + }; + }).then(function () { + throw new Error('wtf') + }, function (err) { + assert(err); + assert(err.message == 'boom'); + }); + }) + }) +}) diff --git a/test/bluebird/invalid.js b/test/bluebird/invalid.js new file mode 100644 index 0000000..5afadef --- /dev/null +++ b/test/bluebird/invalid.js @@ -0,0 +1,18 @@ + +var assert = require('assert'); + +var co = require('../../co-bluebird'); + +describe('yield ', function () { + it('should throw an error', function () { + return co(function* () { + try { + yield null; + throw new Error('lol'); + } catch (err) { + assert(err instanceof TypeError); + assert(~err.message.indexOf('You may only yield')); + } + }) + }) +}) diff --git a/test/bluebird/objects.js b/test/bluebird/objects.js new file mode 100644 index 0000000..70b3436 --- /dev/null +++ b/test/bluebird/objects.js @@ -0,0 +1,86 @@ + +var read = require('mz/fs').readFile; +var assert = require('assert'); + +var co = require('../../co-bluebird'); + +describe('co(* -> yield {})', function(){ + it('should aggregate several promises', function(){ + return co(function *(){ + var a = read('index.js', 'utf8'); + var b = read('LICENSE', 'utf8'); + var c = read('package.json', 'utf8'); + + var res = yield { + a: a, + b: b, + c: c + }; + + assert.equal(3, Object.keys(res).length); + assert(~res.a.indexOf('exports')); + assert(~res.b.indexOf('MIT')); + assert(~res.c.indexOf('devDependencies')); + }); + }) + + it('should noop with no args', function(){ + return co(function *(){ + var res = yield {}; + assert.equal(0, Object.keys(res).length); + }); + }) + + it('should ignore non-thunkable properties', function(){ + return co(function *(){ + var foo = { + name: { first: 'tobi' }, + age: 2, + address: read('index.js', 'utf8'), + tobi: new Pet('tobi'), + now: new Date(), + falsey: false, + nully: null, + undefiney: undefined, + }; + + var res = yield foo; + + assert.equal('tobi', res.name.first); + assert.equal(2, res.age); + assert.equal('tobi', res.tobi.name); + assert.equal(foo.now, res.now); + assert.equal(false, foo.falsey); + assert.equal(null, foo.nully); + assert.equal(undefined, foo.undefiney); + assert(~res.address.indexOf('exports')); + }); + }) + + it('should preserve key order', function(){ + function timedThunk(time){ + return function(cb){ + setTimeout(cb, time); + } + } + + return co(function *(){ + var before = { + sun: timedThunk(30), + rain: timedThunk(20), + moon: timedThunk(10) + }; + + var after = yield before; + + var orderBefore = Object.keys(before).join(','); + var orderAfter = Object.keys(after).join(','); + assert.equal(orderBefore, orderAfter); + }); + }) +}) + +function Pet(name) { + this.name = name; + this.something = function(){}; +} diff --git a/test/bluebird/promises.js b/test/bluebird/promises.js new file mode 100644 index 0000000..ad8f174 --- /dev/null +++ b/test/bluebird/promises.js @@ -0,0 +1,98 @@ + +var assert = require('assert'); +var Promise = require('bluebird'); + +var co = require('../../co-bluebird'); + +function getPromise(val, err) { + return new Promise(function (resolve, reject) { + if (err) reject(err); + else resolve(val); + }); +} + +describe('co(* -> yield ', function(){ + describe('with one promise yield', function(){ + it('should work', function(){ + return co(function *(){ + var a = yield getPromise(1); + assert.equal(1, a); + }); + }) + }) + + describe('with several promise yields', function(){ + it('should work', function(){ + return co(function *(){ + var a = yield getPromise(1); + var b = yield getPromise(2); + var c = yield getPromise(3); + + assert.deepEqual([1, 2, 3], [a, b, c]); + }); + }) + }) + + describe('when a promise is rejected', function(){ + it('should throw and resume', function(){ + var error; + + return co(function *(){ + try { + yield getPromise(1, new Error('boom')); + } catch (err) { + error = err; + } + + assert('boom' == error.message); + var ret = yield getPromise(1); + assert(1 == ret); + }); + }) + }) + + describe('when yielding a non-standard promise-like', function(){ + it('should return a real Promise', function() { + assert(co(function *(){ + yield { then: function(){} }; + }) instanceof Promise); + }); + }) +}) + +describe('co(function) -> promise', function(){ + it('return value', function(done){ + co(function(){ + return 1; + }).then(function(data){ + assert.equal(data, 1); + done(); + }) + }) + + it('return resolve promise', function(){ + return co(function(){ + return Promise.resolve(1); + }).then(function(data){ + assert.equal(data, 1); + }) + }) + + it('return reject promise', function(){ + return co(function(){ + return Promise.reject(1); + }).catch(function(data){ + assert.equal(data, 1); + }) + }) + + it('should catch errors', function(){ + return co(function(){ + throw new Error('boom'); + }).then(function () { + throw new Error('nope'); + }).catch(function (err) { + assert.equal(err.message, 'boom'); + }); + }) +}) diff --git a/test/bluebird/recursion.js b/test/bluebird/recursion.js new file mode 100644 index 0000000..77fd432 --- /dev/null +++ b/test/bluebird/recursion.js @@ -0,0 +1,42 @@ + +var read = require('mz/fs').readFile; +var assert = require('assert'); + +var co = require('../../co-bluebird'); + +describe('co() recursion', function(){ + it('should aggregate arrays within arrays', function(){ + return co(function *(){ + var a = read('index.js', 'utf8'); + var b = read('LICENSE', 'utf8'); + var c = read('package.json', 'utf8'); + + var res = yield [a, [b, c]]; + assert.equal(2, res.length); + assert(~res[0].indexOf('exports')); + assert.equal(2, res[1].length); + assert(~res[1][0].indexOf('MIT')); + assert(~res[1][1].indexOf('devDependencies')); + }); + }) + + it('should aggregate objects within objects', function(){ + return co(function *(){ + var a = read('index.js', 'utf8'); + var b = read('LICENSE', 'utf8'); + var c = read('package.json', 'utf8'); + + var res = yield { + 0: a, + 1: { + 0: b, + 1: c + } + }; + + assert(~res[0].indexOf('exports')); + assert(~res[1][0].indexOf('MIT')); + assert(~res[1][1].indexOf('devDependencies')); + }); + }) +}) diff --git a/test/bluebird/thunks.js b/test/bluebird/thunks.js new file mode 100644 index 0000000..7536f7d --- /dev/null +++ b/test/bluebird/thunks.js @@ -0,0 +1,281 @@ + +var assert = require('assert'); + +var co = require('../../co-bluebird'); + +function get(val, err, error) { + return function(done){ + if (error) throw error; + setTimeout(function(){ + done(err, val); + }, 10); + } +} + +describe('co(* -> yield fn(done))', function () { + describe('with no yields', function(){ + it('should work', function(){ + return co(function *(){}); + }) + }) + + describe('with one yield', function(){ + it('should work', function(){ + return co(function *(){ + var a = yield get(1); + assert.equal(1, a); + }); + }) + }) + + describe('with several yields', function(){ + it('should work', function(){ + return co(function *(){ + var a = yield get(1); + var b = yield get(2); + var c = yield get(3); + + assert.deepEqual([1, 2, 3], [a, b, c]); + }); + }) + }) + + describe('with many arguments', function(){ + it('should return an array', function(){ + function exec(cmd) { + return function(done){ + done(null, 'stdout', 'stderr'); + } + } + + return co(function *(){ + var out = yield exec('something'); + assert.deepEqual(['stdout', 'stderr'], out); + }); + }) + }) + + describe('when the function throws', function(){ + it('should be caught', function(){ + return co(function *(){ + try { + var a = yield get(1, null, new Error('boom')); + } catch (err) { + assert.equal('boom', err.message); + } + }); + }) + }) + + describe('when an error is passed then thrown', function(){ + it('should only catch the first error only', function(){ + return co(function *() { + yield function (done){ + done(new Error('first')); + throw new Error('second'); + } + }).then(function () { + throw new Error('wtf') + }, function(err){ + assert.equal('first', err.message); + }); + }) + }) + + describe('when an error is passed', function(){ + it('should throw and resume', function(){ + var error; + + return co(function *(){ + try { + yield get(1, new Error('boom')); + } catch (err) { + error = err; + } + + assert('boom' == error.message); + var ret = yield get(1); + assert(1 == ret); + }); + }) + }) + + describe('with nested co()s', function(){ + it('should work', function(){ + var hit = []; + + return co(function *(){ + var a = yield get(1); + var b = yield get(2); + var c = yield get(3); + hit.push('one'); + + assert.deepEqual([1, 2, 3], [a, b, c]) + + yield co(function *(){ + hit.push('two'); + var a = yield get(1); + var b = yield get(2); + var c = yield get(3); + + assert.deepEqual([1, 2, 3], [a, b, c]) + + yield co(function *(){ + hit.push('three'); + var a = yield get(1); + var b = yield get(2); + var c = yield get(3); + + assert.deepEqual([1, 2, 3], [a, b, c]) + }); + }); + + yield co(function *(){ + hit.push('four'); + var a = yield get(1); + var b = yield get(2); + var c = yield get(3); + + assert.deepEqual([1, 2, 3], [a, b, c]) + }); + + assert.deepEqual(['one', 'two', 'three', 'four'], hit); + }); + }) + }) + + describe('return values', function(){ + describe('with a callback', function(){ + it('should be passed', function(){ + return co(function *(){ + return [ + yield get(1), + yield get(2), + yield get(3) + ]; + }).then(function (res) { + assert.deepEqual([1, 2, 3], res); + }); + }) + }) + + describe('when nested', function(){ + it('should return the value', function(){ + return co(function *(){ + var other = yield co(function *(){ + return [ + yield get(4), + yield get(5), + yield get(6) + ] + }); + + return [ + yield get(1), + yield get(2), + yield get(3) + ].concat(other); + }).then(function (res) { + assert.deepEqual([1, 2, 3, 4, 5, 6], res); + }); + }) + }) + }) + + describe('when yielding neither a function nor a promise', function(){ + it('should throw', function(){ + var errors = []; + + return co(function *(){ + try { + var a = yield 'something'; + } catch (err) { + errors.push(err.message); + } + + try { + var a = yield 'something'; + } catch (err) { + errors.push(err.message); + } + + assert.equal(2, errors.length); + var msg = 'yield a function, promise, generator, array, or object'; + assert(~errors[0].indexOf(msg)); + assert(~errors[1].indexOf(msg)); + }); + }) + }) + + describe('with errors', function(){ + it('should throw', function(){ + var errors = []; + + return co(function *(){ + try { + var a = yield get(1, new Error('foo')); + } catch (err) { + errors.push(err.message); + } + + try { + var a = yield get(1, new Error('bar')); + } catch (err) { + errors.push(err.message); + } + + assert.deepEqual(['foo', 'bar'], errors); + }); + }) + + it('should catch errors on .send()', function(){ + var errors = []; + + return co(function *(){ + try { + var a = yield get(1, null, new Error('foo')); + } catch (err) { + errors.push(err.message); + } + + try { + var a = yield get(1, null, new Error('bar')); + } catch (err) { + errors.push(err.message); + } + + assert.deepEqual(['foo', 'bar'], errors); + }); + }) + + it('should pass future errors to the callback', function(){ + return co(function *(){ + yield get(1); + yield get(2, null, new Error('fail')); + assert(false); + yield get(3); + }).catch(function(err){ + assert.equal('fail', err.message); + }); + }) + + it('should pass immediate errors to the callback', function(){ + return co(function *(){ + yield get(1); + yield get(2, new Error('fail')); + assert(false); + yield get(3); + }).catch(function(err){ + assert.equal('fail', err.message); + }); + }) + + it('should catch errors on the first invocation', function(){ + return co(function *(){ + throw new Error('fail'); + }).catch(function(err){ + assert.equal('fail', err.message); + }); + }) + }) +}) diff --git a/test/bluebird/wrap.js b/test/bluebird/wrap.js new file mode 100644 index 0000000..db72774 --- /dev/null +++ b/test/bluebird/wrap.js @@ -0,0 +1,28 @@ + +var assert = require('assert'); + +var co = require('../../co-bluebird'); + +describe('co.wrap(fn*)', function () { + it('should pass context', function () { + var ctx = { + some: 'thing' + }; + + return co.wrap(function* () { + assert.equal(ctx, this); + }).call(ctx); + }) + + it('should pass arguments', function () { + return co.wrap(function* (a, b, c) { + assert.deepEqual([1, 2, 3], [a, b, c]); + })(1, 2, 3); + }) + + it('should expose the underlying generator function', function () { + var wrapped = co.wrap(function* (a, b, c) {}); + var source = Object.toString.call(wrapped.__generatorFunction__); + assert(source.indexOf('function*') === 0); + }) +})