| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 |
- /*!
- * Chai - addChainingMethod utility
- * Copyright(c) 2012-2014 Jake Luer <jake@alogicalparadox.com>
- * MIT Licensed
- */
- /*!
- * Module dependencies
- */
- var transferFlags = require('./transferFlags');
- var flag = require('./flag');
- var config = require('../config');
- /*!
- * Module variables
- */
- // Check whether `__proto__` is supported
- var hasProtoSupport = '__proto__' in Object;
- // Without `__proto__` support, this module will need to add properties to a function.
- // However, some Function.prototype methods cannot be overwritten,
- // and there seems no easy cross-platform way to detect them (@see chaijs/chai/issues/69).
- var excludeNames = /^(?:length|name|arguments|caller)$/;
- // Cache `Function` properties
- var call = Function.prototype.call,
- apply = Function.prototype.apply;
- /**
- * ### addChainableMethod (ctx, name, method, chainingBehavior)
- *
- * Adds a method to an object, such that the method can also be chained.
- *
- * utils.addChainableMethod(chai.Assertion.prototype, 'foo', function (str) {
- * var obj = utils.flag(this, 'object');
- * new chai.Assertion(obj).to.be.equal(str);
- * });
- *
- * Can also be accessed directly from `chai.Assertion`.
- *
- * chai.Assertion.addChainableMethod('foo', fn, chainingBehavior);
- *
- * The result can then be used as both a method assertion, executing both `method` and
- * `chainingBehavior`, or as a language chain, which only executes `chainingBehavior`.
- *
- * expect(fooStr).to.be.foo('bar');
- * expect(fooStr).to.be.foo.equal('foo');
- *
- * @param {Object} ctx object to which the method is added
- * @param {String} name of method to add
- * @param {Function} method function to be used for `name`, when called
- * @param {Function} chainingBehavior function to be called every time the property is accessed
- * @name addChainableMethod
- * @api public
- */
- module.exports = function (ctx, name, method, chainingBehavior) {
- if (typeof chainingBehavior !== 'function') {
- chainingBehavior = function () { };
- }
- var chainableBehavior = {
- method: method
- , chainingBehavior: chainingBehavior
- };
- // save the methods so we can overwrite them later, if we need to.
- if (!ctx.__methods) {
- ctx.__methods = {};
- }
- ctx.__methods[name] = chainableBehavior;
- Object.defineProperty(ctx, name,
- { get: function () {
- chainableBehavior.chainingBehavior.call(this);
- var assert = function assert() {
- var old_ssfi = flag(this, 'ssfi');
- if (old_ssfi && config.includeStack === false)
- flag(this, 'ssfi', assert);
- var result = chainableBehavior.method.apply(this, arguments);
- return result === undefined ? this : result;
- };
- // Use `__proto__` if available
- if (hasProtoSupport) {
- // Inherit all properties from the object by replacing the `Function` prototype
- var prototype = assert.__proto__ = Object.create(this);
- // Restore the `call` and `apply` methods from `Function`
- prototype.call = call;
- prototype.apply = apply;
- }
- // Otherwise, redefine all properties (slow!)
- else {
- var asserterNames = Object.getOwnPropertyNames(ctx);
- asserterNames.forEach(function (asserterName) {
- if (!excludeNames.test(asserterName)) {
- var pd = Object.getOwnPropertyDescriptor(ctx, asserterName);
- Object.defineProperty(assert, asserterName, pd);
- }
- });
- }
- transferFlags(this, assert);
- return assert;
- }
- , configurable: true
- });
- };
|