Jump To …

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 this.callback to set the response as the topic

    "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 topic.is and topic().is The function is used to allow topic.is(value)

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 topic().is

Object.defineProperty(Topic, "is", propertyDescriptorIs);

replace topic method with a getter that returns the function make sure to bind this and add the is property to the function

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 vow().it

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 vow.it and vow().it

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 vow().it

Object.defineProperty(Vow, "it", propertyDescriptorIt);

Replace vow method with a getter that returns the function make sure to bind the scope and add it to the vow value

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 name has value val

    "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 exports.Assersion add a wrapper to Should. The wrapper simply saves the command on a stack. The stack is unravelled when the should wrapper ends.

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');
    }
};