diff --git a/lib/api-easy.js b/lib/api-easy.js index ecff452..3d4bb3a 100644 --- a/lib/api-easy.js +++ b/lib/api-easy.js @@ -95,6 +95,7 @@ exports.describe = function (text) { this.port = port || 80; this.secure = options.secure || false; this.auth = options.auth; + this.timeout = options.timeout || 0; // // **TODO _(indexzero)_:** Setup `this.options` here (i.e. options for the SUITE, not the REQUEST) @@ -191,6 +192,10 @@ exports.describe = function (text) { var args = Array.prototype.slice.call(arguments); return this._request.apply(this, ['post'].concat(args)); }, + options: function (/* [uri, data, params] */) { + var args = Array.prototype.slice.call(arguments); + return this._request.apply(this, ['options'].concat(args)); + }, put: function (/* [uri, data, params] */) { var args = Array.prototype.slice.call(arguments); return this._request.apply(this, ['put'].concat(args)); @@ -221,25 +226,29 @@ exports.describe = function (text) { // low memory consumption // fs.readFile(filepath[0], function (err, fileData) { - var multipart = outgoing.multipart = []; - if(data && data.length){ - Object.keys(data[0]).forEach(function(key){ - var value = data[0][key]; - multipart.push({ - 'Content-Disposition': 'form-data; name="' + key + '"', - body: value + if (err) { + callback(err); + } else { + var multipart = outgoing.multipart = []; + if(data && data.length){ + Object.keys(data[0]).forEach(function(key){ + var value = data[0][key]; + multipart.push({ + 'Content-Disposition': 'form-data; name="' + key + '"', + body: value + }); }); + } + + multipart.push({ + 'content-type': 'application/octet-stream', + 'Content-Transfer-Encoding': 'binary', + 'Content-Disposition': 'form-data; name="' + filePartName + '"; filename="' + filename + '"', + 'body': fileData }); - } - multipart.push({ - 'content-type': 'application/octet-stream', - 'Content-Transfer-Encoding': 'binary', - 'Content-Disposition': 'form-data; name="' + filePartName + '"; filename="' + filename + '"', - 'body': fileData - }); - - request(outgoing, callback); + request(outgoing, callback); + } }); }); @@ -268,40 +277,50 @@ exports.describe = function (text) { }); context = this._currentTest(this.current); - + // When using a custom test assertion function, both the assertion function // and a description are required or else we have no key in the JSON structure to use. if (text && !test || test && !text) { throw new Error('Both description and a custom test are required.'); } - + + var withDebugInfo = function withDebugInfo(fun) { + return function(err, res, body) { + try { + return fun(err, res, body); + } catch (e) { + throw new Error(e.toString() + '\n\nContext:\n' + context.debugInfo.method.toUpperCase() + ' ' + context.debugInfo.uri + '\n\n' + context.debugInfo.stacktrace); + } + }; + }; + // Setup the custom test assertion if we have the appropriate arguments. if (text && test) { - context[text] = function (err, res, body) { - assert.isNull(err); + context[text] = withDebugInfo(function (err, res, body) { + assert.ifError(err); test.apply(context, arguments); - }; + }); } - + // Setup the response code test assertion if we have the appropriate arguments. if (code) { - context['should respond with ' + code] = function (err, res, body) { - assert.isNull(err); + context['should respond with ' + code] = withDebugInfo(function (err, res, body) { + assert.ifError(err); assert.equal(res.statusCode, code); - }; + }); } - + // Setup the JSON response assertion if we have the appropriate arguments. if (result) { - context['should respond with ' + JSON.stringify(result).substring(0, 50)] = function (err, res, body) { + context['should respond with ' + JSON.stringify(result).substring(0, 50)] = withDebugInfo(function (err, res, body) { // // Pass any and all errors from parsing and asserting // the JSON returned to the underlying `vows` suite. // - assert.isNull(err); + assert.ifError(err); var testResult = JSON.parse(body); assert.deepEqual(testResult, result); - }; + }); } return this; @@ -424,7 +443,7 @@ exports.describe = function (text) { port = this.port && this.port !== 80 ? ':' + this.port : '', outgoing = clone(this.outgoing), fullUri, context; - + // // Update the fullUri for this request with the passed uri // and the query string parameters (if any). @@ -467,6 +486,7 @@ exports.describe = function (text) { outgoing.uri += this.auth ? this.auth + '@' : ''; outgoing.uri += this.host + port + fullUri; outgoing.method = method; + outgoing.timeout = this.timeout; // // Create the description for this test. This is currently static. @@ -474,7 +494,19 @@ exports.describe = function (text) { // this.current = ['A', method.toUpperCase(), 'to', fullUri].join(' '); context = this._currentTest(); - + + var debugInfo = { + uri: outgoing.uri, + method: outgoing.method, + stacktrace: new Error('original stacktrace').stack + }; + + if (context[this.current]) { + console.log('\nWARNING: A previous test has been overwritten. Is that what you intended?\n' + + '(Hint: To avoid that error, use "next" to start a new batch)\n\n' + + 'Context: ' + this.current + '\n\n' + debugInfo.stacktrace); + } + // // Add the topic for the specified request to the context of the current // batch used by this suite. @@ -495,7 +527,9 @@ exports.describe = function (text) { requestImpl(outgoing, this.callback); else request(outgoing, this.callback); - } + }, + + debugInfo: debugInfo }; // diff --git a/package.json b/package.json index 9c27ad5..e7cfff6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "api-easy", "description": "Fluent (i.e. chainable) syntax for generating vows tests against RESTful APIs.", - "version": "0.3.8", + "version": "0.4.0", "author": "Nodejitsu Inc. ", "maintainers": [ "indexzero " @@ -19,20 +19,20 @@ "dependencies": { "pkginfo": "0.3.x", "request": "2.x.x", - "vows": "0.7.x", + "vows": "0.8.x", "qs": "0.5.x" }, "devDependencies": { - "director": "~1.0.3", - "formidable": "1.0.2", - "mkdirp": "0.2.x" + "director": "~1.2.8", + "formidable": "1.0.17", + "mkdirp": "0.5.x" }, "main": "./lib/api-easy", "scripts": { "test": "vows test/*-test.js --spec" }, "engines": { - "node": ">= 0.4.3" + "node": ">= 0.10.0" } }