diff options
-rw-r--r-- | .jshintrc | 5 | ||||
-rw-r--r-- | .travis.yml | 7 | ||||
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | karma.conf.js | 12 | ||||
-rw-r--r-- | lib/async.js | 17 | ||||
-rw-r--r-- | mocha_test/compose.js | 86 | ||||
-rw-r--r-- | mocha_test/forever.js | 44 | ||||
-rw-r--r-- | mocha_test/support/is_browser.js | 4 | ||||
-rw-r--r-- | package.json | 13 | ||||
-rwxr-xr-x | test/test-async.js | 223 |
10 files changed, 274 insertions, 141 deletions
@@ -21,6 +21,9 @@ "node": true, "globals": { "self": true, - "define": true + "define": true, + "describe": true, + "context": true, + "it": true } } diff --git a/.travis.yml b/.travis.yml index 0a62fca..e225b8f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,4 +3,11 @@ node_js: - "0.10" - "0.12" - "iojs-v2.1.0" +sudo: false after_success: npm run coveralls + +# Needed to run Karma with Firefox on Travis +# http://karma-runner.github.io/0.13/plus/travis.html +before_script: + - export DISPLAY=:99.0 + - sh -e /etc/init.d/xvfb start @@ -23,8 +23,8 @@ clean: rm -rf $(BUILDDIR) lint: - $(JSHINT) $(SRC) test/*.js perf/*.js - $(JSCS) $(SRC) test/*.js perf/*.js + $(JSHINT) $(SRC) test/*.js mocha_test/* perf/*.js + $(JSCS) $(SRC) test/*.js mocha_test/* perf/*.js .PHONY: test lint build all clean diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 0000000..9e048c3 --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,12 @@ +module.exports = function (config) { + config.set({ + browsers: ['Firefox'], + files: ['mocha_test/*.js'], + frameworks: ['browserify', 'mocha'], + preprocessors: { + 'mocha_test/*.js': ['browserify'] + }, + reporters: ['mocha'], + singleRun: true + }); +} diff --git a/lib/async.js b/lib/async.js index f3cfb80..9c2b6af 100644 --- a/lib/async.js +++ b/lib/async.js @@ -62,6 +62,12 @@ return _toString.call(obj) === '[object Array]'; }; + // Ported from underscore.js isObject + var _isObject = function(obj) { + var type = typeof obj; + return type === 'function' || type === 'object' && !!obj; + }; + function _isArrayLike(arr) { return _isArray(arr) || ( // has a positive integer length property @@ -165,7 +171,6 @@ switch (startIndex) { case 0: return func.call(this, rest); case 1: return func.call(this, arguments[0], rest); - case 2: return func.call(this, arguments[0], arguments[1], rest); } // Currently unused but handle cases outside of the switch statement: // var args = Array(startIndex + 1); @@ -593,7 +598,7 @@ acc.times = parseInt(t.times, 10) || DEFAULT_TIMES; acc.interval = parseInt(t.interval, 10) || DEFAULT_INTERVAL; } else { - throw new Error('Unsupported argument type for \'times\': ' + typeof(t)); + throw new Error('Unsupported argument type for \'times\': ' + typeof t); } } @@ -1013,7 +1018,7 @@ function _console_fn(name) { return _restParam(function (fn, args) { fn.apply(null, args.concat([_restParam(function (err, args) { - if (typeof console !== 'undefined') { + if (typeof console === 'object') { if (err) { if (console.error) { console.error(err); @@ -1186,7 +1191,7 @@ return callback(e); } // if result is Promise object - if (typeof result !== 'undefined' && typeof result.then === "function") { + if (_isObject(result) && typeof result.then === "function") { result.then(function(value) { callback(null, value); }).catch(function(err) { @@ -1199,11 +1204,11 @@ }; // Node.js - if (typeof module !== 'undefined' && module.exports) { + if (typeof module === 'object' && module.exports) { module.exports = async; } // AMD / RequireJS - else if (typeof define !== 'undefined' && define.amd) { + else if (typeof define === 'function' && define.amd) { define([], function () { return async; }); diff --git a/mocha_test/compose.js b/mocha_test/compose.js new file mode 100644 index 0000000..27b1869 --- /dev/null +++ b/mocha_test/compose.js @@ -0,0 +1,86 @@ +var async = require('../lib/async'); +var expect = require('chai').expect; + +describe('compose', function(){ + context('all functions succeed', function(){ + it('yields the result of the composition of the functions', function(done){ + var add2 = function (n, cb) { + setTimeout(function () { + cb(null, n + 2); + }); + }; + var mul3 = function (n, cb) { + setTimeout(function () { + cb(null, n * 3); + }); + }; + var add1 = function (n, cb) { + setTimeout(function () { + cb(null, n + 1); + }); + }; + var add2mul3add1 = async.compose(add1, mul3, add2); + add2mul3add1(3, function (err, result) { + expect(err).to.not.exist; + expect(result).to.eql(16); + done(); + }); + }); + }); + + context('a function errors', function(){ + it('yields the error and does not call later functions', function(done){ + var add1called = false; + var mul3error = new Error('mul3 error') + var add2 = function (n, cb) { + setTimeout(function () { + cb(null, n + 2); + }); + }; + var mul3 = function (n, cb) { + setTimeout(function () { + cb(mul3error); + }); + }; + var add1 = function (n, cb) { + add1called = true; + setTimeout(function () { + cb(null, n + 1); + }); + }; + var add2mul3add1 = async.compose(add1, mul3, add2); + add2mul3add1(3, function (err, result) { + expect(err).to.eql(mul3error); + expect(result).to.not.exist; + expect(add1called).to.be.false; + done(); + }); + }); + }); + + it('calls each function with the binding of the composed function', function(done){ + var context = {}; + var add2Context = null; + var mul3Context = null; + var add2 = function (n, cb) { + add2Context = this; + setTimeout(function () { + cb(null, n + 2); + }); + }; + var mul3 = function (n, cb) { + mul3Context = this; + setTimeout(function () { + cb(null, n * 3); + }); + }; + var add2mul3 = async.compose(mul3, add2); + add2mul3.call(context, 3, function (err, result) { + expect(err).to.not.exist; + expect(result).to.eql(15); + expect(add2Context).to.equal(context); + expect(mul3Context).to.equal(context); + done(); + }); + }); +}); diff --git a/mocha_test/forever.js b/mocha_test/forever.js new file mode 100644 index 0000000..970c422 --- /dev/null +++ b/mocha_test/forever.js @@ -0,0 +1,44 @@ +var async = require('../lib/async'); +var expect = require('chai').expect; +var isBrowser = require('./support/is_browser'); + +describe('forever', function(){ + context('function is asynchronous', function(){ + it('executes the function over and over until it yields an error', function(done){ + var counter = 0; + function addOne(callback) { + counter++; + if (counter === 50) { + return callback('too big!'); + } + async.setImmediate(function () { + callback(); + }); + } + async.forever(addOne, function (err) { + expect(err).to.eql('too big!'); + expect(counter).to.eql(50); + done(); + }); + }); + }); + + context('function is synchronous', function(){ + it('does not cause a stack overflow', function(done){ + if (isBrowser()) return done(); // this will take forever in a browser + var counter = 0; + function addOne(callback) { + counter++; + if (counter === 50000) { // needs to be huge to potentially overflow stack in node + return callback('too big!'); + } + callback(); + } + async.forever(addOne, function (err) { + expect(err).to.eql('too big!'); + expect(counter).to.eql(50000); + done(); + }); + }); + }); +}); diff --git a/mocha_test/support/is_browser.js b/mocha_test/support/is_browser.js new file mode 100644 index 0000000..85e1522 --- /dev/null +++ b/mocha_test/support/is_browser.js @@ -0,0 +1,4 @@ +module.exports = function() { + return (typeof process === "undefined") || + (process + "" !== "[object process]"); // browserify +}; diff --git a/package.json b/package.json index 4028e48..d0021ae 100644 --- a/package.json +++ b/package.json @@ -21,12 +21,19 @@ "devDependencies": { "benchmark": "bestiejs/benchmark.js", "bluebird": "^2.9.32", + "chai": "^3.1.0", "coveralls": "^2.11.2", "es6-promise": "^2.3.0", "jscs": "^1.13.1", "jshint": "~2.8.0", + "karma": "^0.13.2", + "karma-browserify": "^4.2.1", + "karma-firefox-launcher": "^0.1.6", + "karma-mocha": "^0.2.0", + "karma-mocha-reporter": "^1.0.2", "lodash": "^3.9.0", "mkdirp": "~0.5.1", + "mocha": "^2.2.5", "native-promise-only": "^0.8.0-a", "nodeunit": ">0.0.0", "nyc": "^2.1.0", @@ -47,7 +54,11 @@ ] }, "scripts": { - "test": "npm run-script lint && nodeunit test/test-async.js", + "mocha-node-test": "mocha mocha_test/", + "mocha-browser-test": "karma start", + "mocha-test": "npm run mocha-node-test && npm run mocha-browser-test", + "nodeunit-test": "nodeunit test/test-async.js", + "test": "npm run-script lint && npm run nodeunit-test && npm run mocha-test", "lint": "jshint lib/*.js test/*.js perf/*.js && jscs lib/*.js test/*.js perf/*.js", "coverage": "nyc npm test && nyc report", "coveralls": "nyc npm test && nyc report --reporter=text-lcov | coveralls" diff --git a/test/test-async.js b/test/test-async.js index fdb949b..dc7d949 100755 --- a/test/test-async.js +++ b/test/test-async.js @@ -1,3 +1,8 @@ +/** + * NOTE: We are in the process of migrating these tests to Mocha. If you are + * adding a new test, consider creating a new spec file in mocha_tests/ + */ + var async = require('../lib/async'); if (!Function.prototype.bind) { @@ -85,50 +90,6 @@ function isBrowser() { (process + "" !== "[object process]"); // browserify } -exports['forever'] = { - - 'async': function (test) { - test.expect(2); - var counter = 0; - function addOne(callback) { - counter++; - if (counter === 50) { - return callback('too big!'); - } - async.setImmediate(function () { - callback(); - }); - } - async.forever(addOne, function (err) { - test.equal(err, 'too big!'); - test.equal(counter, 50); - test.done(); - }); -}, - - 'sync': function (test) { - if (isBrowser()) { - // this will take forever in a browser - return test.done(); - } - test.expect(2); - var counter = 0; - function addOne(callback) { - counter++; - if (counter === 50000) { // needs to be huge to potentially overflow stack in node - return callback('too big!'); - } - callback(); - } - async.forever(addOne, function (err) { - test.equal(err, 'too big!'); - test.equal(counter, 50000); - test.done(); - }); -} - -}; - exports['applyEach'] = function (test) { test.expect(5); var call_order = []; @@ -222,93 +183,6 @@ exports['applyEach partial application'] = function (test) { }); }; -exports['compose'] = function (test) { - test.expect(5); - var add2 = function (n, cb) { - test.equal(n, 3); - setTimeout(function () { - cb(null, n + 2); - }, 50); - }; - var mul3 = function (n, cb) { - test.equal(n, 5); - setTimeout(function () { - cb(null, n * 3); - }, 15); - }; - var add1 = function (n, cb) { - test.equal(n, 15); - setTimeout(function () { - cb(null, n + 1); - }, 100); - }; - var add2mul3add1 = async.compose(add1, mul3, add2); - add2mul3add1(3, function (err, result) { - if (err) { - return test.done(err); - } - test.ok(err === null, err + " passed instead of 'null'"); - test.equal(result, 16); - test.done(); - }); -}; - -exports['compose error'] = function (test) { - test.expect(3); - var testerr = new Error('test'); - - var add2 = function (n, cb) { - test.equal(n, 3); - setTimeout(function () { - cb(null, n + 2); - }, 50); - }; - var mul3 = function (n, cb) { - test.equal(n, 5); - setTimeout(function () { - cb(testerr); - }, 15); - }; - var add1 = function (n, cb) { - test.ok(false, 'add1 should not get called'); - setTimeout(function () { - cb(null, n + 1); - }, 100); - }; - var add2mul3add1 = async.compose(add1, mul3, add2); - add2mul3add1(3, function (err) { - test.equal(err, testerr); - test.done(); - }); -}; - -exports['compose binding'] = function (test) { - test.expect(4); - var testcontext = {name: 'foo'}; - - var add2 = function (n, cb) { - test.equal(this, testcontext); - setTimeout(function () { - cb(null, n + 2); - }, 50); - }; - var mul3 = function (n, cb) { - test.equal(this, testcontext); - setTimeout(function () { - cb(null, n * 3); - }, 15); - }; - var add2mul3 = async.compose(mul3, add2); - add2mul3.call(testcontext, 3, function (err, result) { - if (err) { - return test.done(err); - } - test.equal(this, testcontext); - test.equal(result, 15); - test.done(); - }); -}; - exports['seq'] = function (test) { test.expect(5); var add2 = function (n, cb) { @@ -728,6 +602,19 @@ exports['retry when all attempts succeeds'] = function(test) { }); }; +exports['retry fails with invalid arguments'] = function(test) { + test.throws(function() { + async.retry(""); + }); + test.throws(function() { + async.retry(); + }); + test.throws(function() { + async.retry(function() {}, 2, function() {}); + }); + test.done(); +}; + exports['retry with interval when all attempts succeeds'] = function(test) { var times = 3; var interval = 500; @@ -2465,6 +2352,19 @@ exports['sortBy inverted'] = function(test){ }); }; +exports['sortBy error'] = function(test){ + test.expect(1); + var error = new Error('asdas'); + async.sortBy([{a:1},{a:15},{a:6}], function(x, callback){ + async.setImmediate(function(){ + callback(error); + }); + }, function(err){ + test.equal(err, error); + test.done(); + }); +}; + exports['apply'] = function(test){ test.expect(6); var fn = function(){ @@ -2943,6 +2843,22 @@ exports['doWhilst callback params'] = function (test) { ); }; +exports['doWhilst - error'] = function (test) { + test.expect(1); + var error = new Error('asdas'); + + async.doWhilst( + function (cb) { + cb(error); + }, + function () {}, + function (err) { + test.equal(err, error); + test.done(); + } + ); +}; + exports['during'] = function (test) { var call_order = []; @@ -3002,6 +2918,40 @@ exports['doDuring'] = function (test) { ); }; +exports['doDuring - error test'] = function (test) { + test.expect(1); + var error = new Error('asdas'); + + async.doDuring( + function (cb) { + cb(error); + }, + function () {}, + function (err) { + test.equal(err, error); + test.done(); + } + ); +}; + +exports['doDuring - error iterator'] = function (test) { + test.expect(1); + var error = new Error('asdas'); + + async.doDuring( + function (cb) { + cb(null); + }, + function (cb) { + cb(error); + }, + function (err) { + test.equal(err, error); + test.done(); + } + ); +}; + exports['whilst optional callback'] = function (test) { var counter = 0; async.whilst( @@ -4235,6 +4185,17 @@ exports['asyncify'] = { }); }, + 'asyncify null': function (test) { + var parse = async.asyncify(function() { + return null; + }); + parse("{\"a\":1}", function (err, result) { + test.ok(!err); + test.ok(result === null); + test.done(); + }); + }, + 'variable numbers of arguments': function (test) { async.asyncify(function (x, y, z) { test.ok(arguments.length === 3); |