chai exposes an use
method to access the chai
export and it's utils
.
This method can be used by third parties when creating plugins, but it's also used internally to load it's interface.
The implementation of this method is simple:
exports.use = function (fn) {
if (!~used.indexOf(fn)) {
fn(this, util);
used.push(fn);
}
return this;
};
Internally it uses this to load (among other) the primary Assertion prototype
and the core assertion functionality:
var assertion = require('./chai/assertion'); // primary Assertion prototype
exports.use(assertion); // load it
var core = require('./chai/core/assertions'); // core assertion functionality
exports.use(core); // load it
One of the methods that are exposed by the Assertion prototype
is the addProperty
method which allows you to add properties to said prototype
.
Internally chai
uses this method to add the core assertion functionality to the Assertion prototype
. For instance, all language chains and assertion helpers (exist
, empty
, etc) are added this way.
Language chains:
[ 'to', 'be', 'been'
, 'is', 'and', 'has', 'have'
, 'with', 'that', 'which', 'at'
, 'of', 'same' ].forEach(function (chain) {
Assertion.addProperty(chain, function () {
return this;
});
});
All this functionality becomes available when a specific interface gets loaded internally, for instance expect
. When this interface is loaded, a new Assertion prototype
will be instantiated whenever expect
gets executed, which will contain all functionality:
// load expect interface
var expect = require('./chai/interface/expect'); // expect interface
exports.use(expect); // load it
// expect interface
module.exports = function (chai, util) {
chai.expect = function (val, message) {
return new chai.Assertion(val, message); // return new Assertion Object with all functionality
};
};
As you can see the expect
method accepts a val
argument (and an optional message
argument). When this method is called (for instance expect(foo)
) a new Assertion prototype
will be instantiated and returned, exposing all core functionality (allowing you to do expect(foo).to.exist
).
The Assertion Constructor
uses the flag
util
to set a flag value on the Object that maps to the passed in val
argument.
function Assertion (obj, msg, stack) {
flag(this, 'ssfi', stack || arguments.callee);
flag(this, 'object', obj); // the 'object' flag maps to the passed in val
flag(this, 'message', msg);
}
All exist
then does, is get this value through the flag
util
and evaluates if it not equals to null
, using the assert
method defined on the Assertion prototype
.
Assertion.addProperty('exist', function () {
this.assert(
null != flag(this, 'object')
, 'expected #{this} to exist'
, 'expected #{this} to not exist'
);
});