123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- /**
- * Module dependencies.
- */
- var request = require('superagent')
- , util = require('util')
- , http = require('http')
- , https = require('https')
- , assert = require('assert')
- , Request = request.Request;
- /**
- * Expose `Test`.
- */
- module.exports = Test;
- /**
- * Initialize a new `Test` with the given `app`,
- * request `method` and `path`.
- *
- * @param {Server} app
- * @param {String} method
- * @param {String} path
- * @api public
- */
- function Test(app, method, path) {
- Request.call(this, method, path);
- this.redirects(0);
- this.buffer();
- this.app = app;
- this._asserts = [];
- this.url = 'string' == typeof app
- ? app + path
- : this.serverAddress(app, path);
- }
- /**
- * Inherits from `Request.prototype`.
- */
- Test.prototype.__proto__ = Request.prototype;
- /**
- * Returns a URL, extracted from a server.
- *
- * @param {Server} app
- * @param {String} path
- * @returns {String} URL address
- * @api private
- */
- Test.prototype.serverAddress = function(app, path){
- var addr = app.address();
- if (!addr) this._server = app.listen(0);
- var port = app.address().port;
- var protocol = app instanceof https.Server ? 'https' : 'http';
- return protocol + '://127.0.0.1:' + port + path;
- };
- /**
- * Expectations:
- *
- * .expect(200)
- * .expect(200, fn)
- * .expect(200, body)
- * .expect('Some body')
- * .expect('Some body', fn)
- * .expect('Content-Type', 'application/json')
- * .expect('Content-Type', 'application/json', fn)
- * .expect(fn)
- *
- * @return {Test}
- * @api public
- */
- Test.prototype.expect = function(a, b, c){
- // callback
- if ('function' == typeof a) {
- this._asserts.push(a);
- return this;
- }
- if ('function' == typeof b) this.end(b);
- if ('function' == typeof c) this.end(c);
- // status
- if ('number' == typeof a) {
- this._asserts.push(this._assertStatus.bind(this, a));
- // body
- 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) {
- this._asserts.push(this._assertHeader.bind(this, {name: ''+a, value: b}));
- return this;
- }
- // body
- this._asserts.push(this._assertBody.bind(this, a));
- return this;
- };
- /**
- * Defer invoking superagent's `.end()` until
- * the server is listening.
- *
- * @param {Function} fn
- * @api public
- */
- Test.prototype.end = function(fn){
- var self = this;
- var server = this._server;
- var end = Request.prototype.end;
- end.call(this, function(err, res){
- if (server && server._handle) return server.close(assert);
- assert();
- function assert(){
- self.assert(err, res, fn);
- }
- });
- return this;
- };
- /**
- * 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 error;
- // 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);
- }
- }
- }
- };
- /**
- * 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 + '"');
- }
- };
- /**
- * Perform assertions on the response status and return an Error upon failure.
- *
- * @param {Number} status
- * @param {Response} res
- * @return {?Error}
- * @api private
- */
- 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 + '"');
- }
- };
- /**
- * 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;
- }
- if (err instanceof Error) return err;
- };
- /**
- * Return an `Error` with `msg` and results properties.
- *
- * @param {String} msg
- * @param {Mixed} expected
- * @param {Mixed} actual
- * @return {Error}
- * @api private
- */
- function error(msg, expected, actual) {
- var err = new Error(msg);
- err.expected = expected;
- err.actual = actual;
- err.showDiff = true;
- return err;
- }
|