Skip to content

Commit

Permalink
Merge pull request ladjs#239 from Rob--W/ordered-expectations
Browse files Browse the repository at this point in the history
Run assertions in call order.
  • Loading branch information
mikelax committed Aug 21, 2015
2 parents add067c + 5f238d2 commit a5642ea
Show file tree
Hide file tree
Showing 2 changed files with 288 additions and 81 deletions.
182 changes: 103 additions & 79 deletions lib/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ function Test(app, method, path) {
this.redirects(0);
this.buffer();
this.app = app;
this._fields = {};
this._bodies = [];
this._asserts = [];
this.url = 'string' == typeof app
? app + path
Expand Down Expand Up @@ -78,8 +76,6 @@ Test.prototype.serverAddress = function(app, path){
*/

Test.prototype.expect = function(a, b, c){
var self = this;

// callback
if ('function' == typeof a) {
this._asserts.push(a);
Expand All @@ -90,21 +86,21 @@ Test.prototype.expect = function(a, b, c){

// status
if ('number' == typeof a) {
this._status = a;
this._asserts.push(this._assertStatus.bind(this, a));
// body
if ('function' != typeof b && arguments.length > 1) this._bodies.push(b);
if ('function' != typeof b && arguments.length > 1)
this._asserts.push(this._assertBody.bind(this, b));
return this;
}

// header field
if ('string' == typeof b || 'number' == typeof b || b instanceof RegExp) {
if (!this._fields[a]) this._fields[a] = [];
this._fields[a].push(b);
this._asserts.push(this._assertHeader.bind(this, {name: ''+a, value: b}));
return this;
}

// body
this._bodies.push(a);
this._asserts.push(this._assertBody.bind(this, a));

return this;
};
Expand Down Expand Up @@ -136,95 +132,123 @@ Test.prototype.end = function(fn){
};

/**
* Perform assertions and invoke `fn(err)`.
* Perform assertions and invoke `fn(err, res)`.
*
* @param {?Error} resError
* @param {Response} res
* @param {Function} fn
* @api private
*/

Test.prototype.assert = function(resError, res, fn){
var status = this._status
, fields = this._fields
, bodies = this._bodies
, expecteds
, actual
, re;
var error;

// body
for (var i = 0; i < bodies.length; i++) {
var body = bodies[i];
var isregexp = body instanceof RegExp;
// parsed
if ('object' == typeof body && !isregexp) {
try {
assert.deepEqual(body, res.body);
} catch (err) {
var a = util.inspect(body);
var b = util.inspect(res.body);
return fn(error('expected ' + a + ' response body, got ' + b, body, res.body), res);
}
} else {
// string
if (body !== res.text) {
var a = util.inspect(body);
var b = util.inspect(res.text);

// regexp
if (isregexp) {
if (!body.test(res.text)) {
return fn(error('expected body ' + b + ' to match ' + body, body, res.body), res);
}
} else {
return fn(error('expected ' + a + ' response body, got ' + b, body, res.body), res);
// asserts
for (var i = 0; i < this._asserts.length && !error; ++i) {
error = this._assertFunction(this._asserts[i], res);
}

// set unexpected superagent error if no other error has occurred.
if (!error && resError instanceof Error &&
(!res || resError.status !== res.status))
error = resError;

fn.call(this, error || null, res);
};

/**
* Perform assertions on a response body and return an Error upon failure.
*
* @param {Mixed} body
* @param {Response} res
* @return {?Error}
* @api private
*/

Test.prototype._assertBody = function(body, res) {
var isregexp = body instanceof RegExp;
// parsed
if ('object' == typeof body && !isregexp) {
try {
assert.deepEqual(body, res.body);
} catch (err) {
var a = util.inspect(body);
var b = util.inspect(res.body);
return error('expected ' + a + ' response body, got ' + b, body, res.body);
}
} else {
// string
if (body !== res.text) {
var a = util.inspect(body);
var b = util.inspect(res.text);

// regexp
if (isregexp) {
if (!body.test(res.text)) {
return error('expected body ' + b + ' to match ' + body, body, res.body);
}
} else {
return error('expected ' + a + ' response body, got ' + b, body, res.body);
}
}
}
};

// fields
for (var field in fields) {
expecteds = fields[field];
actual = res.header[field.toLowerCase()];
if (null == actual) return fn(new Error('expected "' + field + '" header field'), res);
for (var i = 0; i < expecteds.length; i++) {
var fieldExpected = expecteds[i];
if (fieldExpected == actual) continue;
if (fieldExpected instanceof RegExp) re = fieldExpected;
if (re && re.test(actual)) continue;
if (re) return fn(new Error('expected "' + field + '" matching ' + fieldExpected + ', got "' + actual + '"'), res);
return fn(new Error('expected "' + field + '" of "' + fieldExpected + '", got "' + actual + '"'), res);
}
/**
* Perform assertions on a response header and return an Error upon failure.
*
* @param {Object} header
* @param {Response} res
* @return {?Error}
* @api private
*/

Test.prototype._assertHeader = function(header, res) {
var field = header.name;
var actual = res.header[field.toLowerCase()];
if (null == actual) return new Error('expected "' + field + '" header field');
var fieldExpected = header.value;
if (fieldExpected == actual) return;
if (fieldExpected instanceof RegExp) {
if (!fieldExpected.test(actual)) return new Error('expected "' + field + '" matching ' + fieldExpected + ', got "' + actual + '"');
} else {
return new Error('expected "' + field + '" of "' + fieldExpected + '", got "' + actual + '"');
}
};

// status
if (status) {
if (res.status !== status) {
var a = http.STATUS_CODES[status];
var b = http.STATUS_CODES[res.status];
return fn(new Error('expected ' + status + ' "' + a + '", got ' + res.status + ' "' + b + '"'), res);
}
/**
* Perform assertions on the response status and return an Error upon failure.
*
* @param {Number} status
* @param {Response} res
* @return {?Error}
* @api private
*/

// remove expected superagent error
if (resError && resError instanceof Error && resError.status === status) {
resError = null;
}
Test.prototype._assertStatus = function(status, res) {
if (res.status !== status) {
var a = http.STATUS_CODES[status];
var b = http.STATUS_CODES[res.status];
return new Error('expected ' + status + ' "' + a + '", got ' + res.status + ' "' + b + '"');
}
};

// asserts
for (var i = 0; i < this._asserts.length; i++) {
var check = this._asserts[i];
var err;
try {
err = check(res);
} catch(e) {
err = e;
}
if (!(err instanceof Error)) continue;
return fn(err instanceof Error ? err : new Error(err), res)
/**
* Performs an assertion by calling a function and return an Error upon failure.
*
* @param {Function} fn
* @param {Response} res
* @return {?Error}
* @api private
*/
Test.prototype._assertFunction = function(check, res) {
var err;
try {
err = check(res);
} catch(e) {
err = e;
}

fn.call(this, resError, res);
if (err instanceof Error) return err;
};

/**
Expand Down
Loading

0 comments on commit a5642ea

Please sign in to comment.