UNPKG

express

Version:

Sinatra inspired web development framework

529 lines (445 loc) 10.3 kB
/*! * Should * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> * MIT Licensed */ /** * Module dependencies. */ var util = require('sys') , assert = require('assert') , AssertionError = assert.AssertionError , eql = require('./eql') , i = util.inspect; /** * Expose assert as should. * * This allows you to do things like below * without require()ing the assert module. * * should.equal(foo.bar, undefined); * */ exports = module.exports = assert; /** * Library version. */ exports.version = '0.0.3'; /** * Expose api via `Object#should`. * * @api public */ Object.defineProperty(Object.prototype, 'should', { set: function(){}, get: function(){ return new Assertion(this); } }); /** * Initialize a new `Assertion` with the given _obj_. * * @param {Mixed} obj * @api private */ var Assertion = exports.Assertion = function Assertion(obj) { this.obj = obj; }; /** * Prototype. */ Assertion.prototype = { /** * Assert _expr_ with the given _msg_ and _negatedMsg_. * * @param {Boolean} expr * @param {String} msg * @param {String} negatedMsg * @api private */ assert: function(expr, msg, negatedMsg){ var msg = this.negate ? negatedMsg : msg , ok = this.negate ? !expr : expr; if (!ok) { throw new AssertionError({ message: msg , stackStartFunction: this.assert }); } }, /** * Dummy getter. * * @api public */ get an() { return this; }, /** * Dummy getter. * * @api public */ get and() { return this; }, /** * Dummy getter. * * @api public */ get be() { return this; }, /** * Dummy getter. * * @api public */ get have() { return this; }, /** * Dummy getter. * * @api public */ get with() { return this; }, /** * Inclusion modifier. * * @api public */ get include() { this.includes = true; return this; }, /** * Negation modifier. * * @api public */ get not() { this.negate = true; return this; }, /** * Get object inspection string. * * @return {String} * @api private */ get inspect() { return i(this.obj); }, /** * Assert instanceof `Arguments`. * * @api public */ get arguments() { this.assert( '[object Arguments]' == Object.prototype.toString.call(this.obj) , 'expected ' + this.inspect + ' to be arguments' , 'expected ' + this.inspect + ' to not be arguments'); return this; }, /** * Assert that an object is empty aka length of 0. * * @api public */ get empty() { this.obj.should.have.property('length'); this.assert( 0 === this.obj.length , 'expected ' + this.inspect + ' to be empty' , 'expected ' + this.inspect + ' not to be empty'); return this; }, /** * Assert true. * * @api public */ get true() { this.assert( true === this.obj , 'expected ' + this.inspect + ' to be true' , 'expected ' + this.inspect + ' not to be true'); return this; }, /** * Assert false. * * @api public */ get false() { this.assert( false === this.obj , 'expected ' + this.inspect + ' to be false' , 'expected ' + this.inspect + ' not to be false'); return this; }, /** * Assert equal. * * @param {Mixed} val * @api public */ eql: function(val){ this.assert( eql(val, this.obj) , 'expected ' + this.inspect + ' to equal ' + i(val) , 'expected ' + this.inspect + ' to not equal ' + i(val)); return this; }, /** * Assert strict equal. * * @param {Mixed} val * @api public */ equal: function(val){ this.assert( val === this.obj , 'expected ' + this.inspect + ' to equal ' + i(val) , 'expected ' + this.inspect + ' to not equal ' + i(val)); return this; }, /** * Assert within start to finish (inclusive). * * @param {Number} start * @param {Number} finish * @api public */ within: function(start, finish){ var range = start + '..' + finish; this.assert( this.obj >= start && this.obj <= finish , 'expected ' + this.inspect + ' to be within ' + range , 'expected ' + this.inspect + ' to not be within ' + range); return this; }, /** * Assert typeof. * * @api public */ a: function(type){ this.assert( type == typeof this.obj , 'expected ' + this.inspect + ' to be a ' + type , 'expected ' + this.inspect + ' not to be a ' + type); return this; }, /** * Assert instanceof. * * @api public */ instanceof: function(constructor){ var name = constructor.name; this.assert( this.obj instanceof constructor , 'expected ' + this.inspect + ' to be an instance of ' + name , 'expected ' + this.inspect + ' not to be an instance of ' + name); return this; }, /** * Assert numeric value above _n_. * * @param {Number} n * @api public */ above: function(n){ this.assert( this.obj > n , 'expected ' + this.inspect + ' to be above ' + n , 'expected ' + this.inspect + ' to be below ' + n); return this; }, /** * Assert numeric value below _n_. * * @param {Number} n * @api public */ below: function(n){ this.assert( this.obj < n , 'expected ' + this.inspect + ' to be below ' + n , 'expected ' + this.inspect + ' to be above ' + n); return this; }, /** * Assert string value matches _regexp_. * * @param {RegExp} regexp * @api public */ match: function(regexp){ this.assert( regexp.exec(this.obj) , 'expected ' + this.inspect + ' to match ' + regexp , 'expected ' + this.inspect + ' not to match ' + regexp); return this; }, /** * Assert property "length" exists and has value of _n_. * * @param {Number} n * @api public */ length: function(n){ this.obj.should.have.property('length'); var len = this.obj.length; this.assert( n == len , 'expected ' + this.inspect + ' to have a length of ' + n + ' but got ' + len , 'expected ' + this.inspect + ' to not have a length of ' + len); return this; }, /** * Assert substring. * * @param {String} str * @api public */ string: function(str){ this.obj.should.be.a('string'); this.assert( ~this.obj.indexOf(str) , 'expected ' + this.inspect + ' to include ' + i(str) , 'expected ' + this.inspect + ' to not include ' + i(str)); return this; }, /** * Assert property _name_ exists, with optional _val_. * * @param {String} name * @param {Mixed} val * @api public */ property: function(name, val){ if (this.negate && undefined !== val) { if (undefined === this.obj[name]) { throw new Error(this.inspect + ' has no property ' + i(name)); } } else { this.assert( undefined !== this.obj[name] , 'expected ' + this.inspect + ' to have a property ' + i(name) , 'expected ' + this.inspect + ' to not have a property ' + i(name)); } if (undefined !== val) { this.assert( val === this.obj[name] , 'expected ' + this.inspect + ' to have a property ' + i(name) + ' of ' + i(val) + ', but got ' + i(this.obj[name]) , 'expected ' + this.inspect + ' to not have a property ' + i(name) + ' of ' + i(val)); } this.obj = this.obj[name]; return this; }, /** * Assert own property _name_ exists. * * @param {String} name * @api public */ ownProperty: function(name){ this.assert( this.obj.hasOwnProperty(name) , 'expected ' + this.inspect + ' to have own property ' + i(name) , 'expected ' + this.inspect + ' to not have own property ' + i(name)); return this; }, /** * Assert that the array contains _obj_. * * @param {Mixed} obj * @api public */ contain: function(obj){ this.obj.should.be.an.instanceof(Array); this.assert( ~this.obj.indexOf(obj) , 'expected ' + this.inspect + ' to contain ' + i(obj) , 'expected ' + this.inspect + ' to not contain ' + i(obj)); return this; }, /** * Assert exact keys or inclusion of keys by using * the `.include` modifier. * * @param {Array|String ...} keys * @api public */ keys: function(keys){ var str , ok = true; keys = keys instanceof Array ? keys : Array.prototype.slice.call(arguments); if (!keys.length) throw new Error('keys required'); var actual = Object.keys(this.obj) , len = keys.length; // Inclusion ok = keys.every(function(key){ return ~actual.indexOf(key); }); // Strict if (!this.negate && !this.includes) { ok = ok && keys.length == actual.length; } // Key string if (len > 1) { keys = keys.map(function(key){ return i(key); }); var last = keys.pop(); str = keys.join(', ') + ', and ' + last; } else { str = i(keys[0]); } // Form str = (len > 1 ? 'keys ' : 'key ') + str; // Have / include str = (this.includes ? 'include ' : 'have ') + str; // Assertion this.assert( ok , 'expected ' + this.inspect + ' to ' + str , 'expected ' + this.inspect + ' to not ' + str); return this; }, /** * Assert that _method_ is a function. * * @param {String} method * @api public */ respondTo: function(method){ this.assert( 'function' == typeof this.obj[method] , 'expected ' + this.inspect + ' to respond to ' + method + '()' , 'expected ' + this.inspect + ' to not respond to ' + method + '()'); return this; } }; /** * Aliases. */ (function alias(name, as){ Assertion.prototype[as] = Assertion.prototype[name]; return alias; }) ('length', 'lengthOf') ('keys', 'key') ('ownProperty', 'haveOwnProperty') ('above', 'greaterThan') ('below', 'lessThan');