Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

From chai's api you've got code like this:

.exist

Asserts that the target is neither null nor undefined.

var foo = 'hi'
  , bar = null
  , baz;

expect(foo).to.exist;
expect(bar).to.not.exist;
expect(baz).to.not.exist;

How does that exist part work? The expect function returns an object, then there's simply a property lookup on the "to" object. That's simply a property evaluation though isn't it? The only thing that would make sense to me is if the exist property is a getter method.

What's the go?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
270 views
Welcome To Ask or Share your Answers For Others

1 Answer

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...