vows-is.js | |
---|---|
var fluent = require("vows-fluent"),
request = require("request"),
should = require("should"),
EventEmitter = require("events").EventEmitter.Prototype,
AssertionConstructor = should.Assertion,
Assertion = AssertionConstructor.prototype,
Trait = require("traits").Trait,
Topic = fluent.Topic,
Vow = fluent.Vow,
Context = fluent.Context; | |
Remove should from the prototype. | delete Object.prototype.should; |
Redefine should in a friendly manner. | Object.defineProperty(Object.prototype, "should", {
get: function() {
return this._should || new AssertionConstructor(this);
},
set: function(val) {
this._should = val;
},
configurable: true
}); |
defaults objects overwritten through config. | var defaults = {}, |
test whether an url is an url or has a method in it. | test_uri = /^(GET|POST|PUT|HEAD|DELETE)(.*)/; |
utility create function | var create = function _create(trait) {
return Object.create(Object.prototype, trait)
}; |
factory to get a server instance. Cached internally | var getServer = function _getServer(cb) {
if (getServer.app) {
cb(getServer.app);
} else {
defaults.server.factory(function(app) {
getServer.app = app;
cb(app);
});
}
} |
Is object used in topic().is | exports.Is = { |
dummy getter | get a() {
return this;
}, |
dummy getter | get an() {
return this;
}, |
invocation. This will invoke the previous topic with values and return it | "invocation": function _invocation() {
var args = arguments;
this._parent.set(function _topic(topic) {
return topic.apply(null, args);
});
return this.end();
}, |
request. This sets the value of the topic to be a request function.
It goes off and asks for a server from the server factory,
Then generates the url to request. It makes a request to your
server then uses | "request": function _request(url) {
this._parent.set(function _topic() {
var cb = this.callback;
getServer(function _getServer(server) {
var options;
if (typeof url === "string") {
options = {};
var match = url && url.match(test_uri);
if (match) {
options.method = match[1];
options.uri = defaults.server.uri + match[2].substr(1);
} else {
options.uri = defaults.server.uri + uri
}
} else {
options = url;
}
request(options, function _handleRequest(err, res, body) {
cb(err, res);
});
});
});
return this.end()
}, |
Set the topic to be a property of the current topic | "property": function _property(name) {
this._parent.set(function _topic(topic) {
return topic[name];
});
return this.end()
}, |
end the is abstraction returning the context owning the topic. | "end": function _end() {
return this._parent.parent()
}
}; |
Property descriptor for property is.
Creates a Is, sets the parent and returns it.
Typeof function check supports | var propertyDescriptorIs = {
"set": function _set() {},
"get": function _get() {
var i = create(Trait(exports.Is));
if (typeof this === "function") {
i._parent = this();
} else {
i._parent = this;
}
var f = (function _is(val) {
if (typeof val === "function") {
var _val = val;
val = function() {
try {
return _val.apply(this, arguments);
} catch (e) {
var ev = Object.create(EventEmitter);
process.nextTick(function() {
ev.emit('error', e)
});
return ev;
}
};
}
this._parent.set(val);
return this.end();
}).bind(i);
var keys = Object.getOwnPropertyNames(i);
keys.forEach(function(key) {
var pd = Object.getOwnPropertyDescriptor(i, key);
Object.defineProperty(f, key, pd);
});
return f;
}
}; |
Add is as a getter to the topic to support | Object.defineProperty(Topic, "is", propertyDescriptorIs); |
replace topic method with a getter that returns the function
make sure to bind this and add the | var old_topic = Context.topic;
Object.defineProperty(Context, "topic", {
"set": function _set() {},
"get": function _get() {
var f = old_topic.bind(this);
Object.defineProperty(f, "is", propertyDescriptorIs);
return f;
}
}); |
It object used in | exports.It = { |
Creates a should wrapper and returns it. Also sets the vow value to unwrap the should wrapper. | get should() {
var s = create(Trait(exports.Should));
s._stack = [];
s._parent = this._parent;
return s;
}
} |
Property descriptor for property it.
Creates a It, sets the parent and returns it.
Typeof function check supports | var propertyDescriptorIt = {
"set": function _set() {},
"get": function _get() {
var i = create(Trait(exports.It));
if (typeof this === "function") {
i._parent = this();
} else {
i._parent = this;
}
return i;
}
}; |
Add is as a getter on Vow to support | Object.defineProperty(Vow, "it", propertyDescriptorIt); |
Replace vow method with a getter that returns the function
make sure to bind the scope and add | var old_vow = Context.vow
Object.defineProperty(Context, "vow", {
"set": function _set() {},
"get": function _get() {
var f = old_vow.bind(this);
Object.defineProperty(f, "it", propertyDescriptorIt);
return f;
}
}); |
Extension of the should.js Assertion object | exports.Assertion = create(Trait.override(Trait({ |
header. Assert it has property headers and that the
header | "header": function _header(name, val) {
return this.property("headers").with.property(name, val);
}, |
error getter. Set's the object to the error and calls ok. | get error() {
this.obj = this._error
return this.ok;
},
}), Trait(Assertion))); |
a Should object which is a wrapper around should.js | exports.Should = { |
transforms the stack into a nice assertion string | "prettyPrint": function _prettyPrint() {
var arr = this._stack.map(function _map(val) {
var str = val.key;
if (exports.prettyPrint[val.key]) {
str += " " + exports.prettyPrint[val.key](val.args);
} else if (val.args.length) {
str += " " + val.args.join(" ");
}
return str;
});
arr.unshift("should");
return arr.join(" ");
}, |
end the abstraction. This creates a should.js assertion and unravels the stack running every assertion. it then returns context that the vow belongs to. | "end": function _end() {
var s = create(Trait(exports.Assertion));
s.obj = this._obj;
s._error = this._error;
this._stack.forEach(function _each(val) {
if (val.type === 'get') {
s = s[val.key];
} else {
s = s[val.key].apply(s, val.args);
}
});
return this._parent.parent();
}
}; |
A stack of prettyPrint methods to make the assertions look pretty | exports.prettyPrint = {
"header": function _property(args) {
return args[0] + " with value " + args[1];
}
}; |
For each of the methods that can be called on a context add them to should. This tells the Should wrappers to end itself and return back to the context. | ["vow", "context", "parent", "batch", "suite", "partial"].forEach(function _each(key) {
exports.Should[key] = function _parent() {
var parent = this._parent.parent();
if (!this._parent._name) {
this._parent.name(this.prettyPrint());
}
var that = this;
this._parent.set(function _set(err, value) {
that._obj = value;
that._error = err;
that.end();
});
return parent[key].apply(parent, arguments);
};
}); |
Replace the vow method of Should with a getter so chaining doesn't break | var should_vow = exports.Should.vow
Object.defineProperty(exports.Should, "vow", {
"set": function _set() {},
"get": function _get() {
var f = should_vow.bind(this);
Object.defineProperty(f, "it", propertyDescriptorIt);
return f;
}
}); |
For each of the methods of | Object.keys(exports.Assertion).forEach(function _each(key) {
if (!exports.Should.hasOwnProperty(key)) {
var descriptor = Object.getOwnPropertyDescriptor(exports.Assertion, key);
var name = descriptor.get ? "get" : "value";
descriptor[name] = function _descriptor() {
this._stack.push({
"type": name,
"key": key,
"args": Array.prototype.slice.call(arguments)
});
return this;
}
Object.defineProperty(exports.Should, key, descriptor);
}
}); |
Config method sets defaults | exports.config = function _init(obj) {
Object.keys(obj).forEach(function _each(key) {
defaults[key] = obj[key];
});
if (defaults.server.defaults) {
request = request.defaults(defaults.server.defaults);
}
return this;
}; |
partial method that runs a predefined partial function on the context | Context.partial = function _partial(name, data) {
var p = exports._partial[name];
return p(this, data);
}; |
Store hashes of partials | exports._partial = {}; |
Store a partial by name | exports.partial = function _partial(name, f) {
this._partial[name] = f;
return this;
}; |
Expose methods of vows-fluent on exports. | Object.keys(fluent).forEach(function _each(key) {
exports[key] = fluent[key];
}); |
Expose the custom vows-is reporter | exports.reporter = require("./reporter.js"); |
Expose vows console | exports.console = require("../lib/console.js"); |
End function will various clean up functions default.server.kill is your clean up function to kill the server | exports.end = function _end() {
if (defaults.server &&
defaults.server.kill &&
typeof defaults.server.kill === "function"
) {
getServer(function(server) {
defaults.server.kill(server);
});
}
}; |
expose should object from should.js | exports.should = should; |
Punch vows in the face | var stylize = exports.console.stylize;
var inspect = exports.console.inspect;
require('assert').AssertionError.prototype.toString = function () {
var that = this,
source = this.stack && this.stack.match(/([a-zA-Z0-9._-]+\.js)(:\d+):\d+/);
function parse(str) {
return str.replace(/{actual}/g, inspect(that.actual)).
replace(/{operator}/g, stylize(that.operator, 'bold')).
replace(/{expected}/g, (that.expected instanceof Function)
? that.expected.name
: inspect(that.expected));
}
if (this.message) {
var msg = stylize(parse(this.message), 'yellow');
if (source) {
msg += stylize(' // ' + source[1] + source[2], 'grey');
}
return msg;
} else {
return stylize([
this.expected,
this.operator,
this.actual
].join(' '), 'yellow');
}
};
|