%PDF- %PDF-
Direktori : /home/opti/workflow/node_modules/chai/lib/chai/utils/ |
Current File : /home/opti/workflow/node_modules/chai/lib/chai/utils/addChainableMethod.js |
/*! * Chai - addChainingMethod utility * Copyright(c) 2012-2014 Jake Luer <jake@alogicalparadox.com> * MIT Licensed */ /*! * Module dependencies */ var addLengthGuard = require('./addLengthGuard'); var chai = require('../../chai'); var flag = require('./flag'); var proxify = require('./proxify'); var transferFlags = require('./transferFlags'); /*! * Module variables */ // Check whether `Object.setPrototypeOf` is supported var canSetPrototype = typeof Object.setPrototypeOf === 'function'; // Without `Object.setPrototypeOf` support, this module will need to add properties to a function. // However, some of functions' own props are not configurable and should be skipped. var testFn = function() {}; var excludeNames = Object.getOwnPropertyNames(testFn).filter(function(name) { var propDesc = Object.getOwnPropertyDescriptor(testFn, name); // Note: PhantomJS 1.x includes `callee` as one of `testFn`'s own properties, // but then returns `undefined` as the property descriptor for `callee`. As a // workaround, we perform an otherwise unnecessary type-check for `propDesc`, // and then filter it out if it's not an object as it should be. if (typeof propDesc !== 'object') return true; return !propDesc.configurable; }); // 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 * @namespace Utils * @name addChainableMethod * @api public */ module.exports = function addChainableMethod(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 chainableMethodGetter() { chainableBehavior.chainingBehavior.call(this); var chainableMethodWrapper = function () { // Setting the `ssfi` flag to `chainableMethodWrapper` causes this // function to be the starting point for removing implementation // frames from the stack trace of a failed assertion. // // However, we only want to use this function as the starting point if // the `lockSsfi` flag isn't set. // // If the `lockSsfi` flag is set, then this assertion is being // invoked from inside of another assertion. In this case, the `ssfi` // flag has already been set by the outer assertion. // // Note that overwriting a chainable method merely replaces the saved // methods in `ctx.__methods` instead of completely replacing the // overwritten assertion. Therefore, an overwriting assertion won't // set the `ssfi` or `lockSsfi` flags. if (!flag(this, 'lockSsfi')) { flag(this, 'ssfi', chainableMethodWrapper); } var result = chainableBehavior.method.apply(this, arguments); if (result !== undefined) { return result; } var newAssertion = new chai.Assertion(); transferFlags(this, newAssertion); return newAssertion; }; addLengthGuard(chainableMethodWrapper, name, true); // Use `Object.setPrototypeOf` if available if (canSetPrototype) { // Inherit all properties from the object by replacing the `Function` prototype var prototype = Object.create(this); // Restore the `call` and `apply` methods from `Function` prototype.call = call; prototype.apply = apply; Object.setPrototypeOf(chainableMethodWrapper, prototype); } // Otherwise, redefine all properties (slow!) else { var asserterNames = Object.getOwnPropertyNames(ctx); asserterNames.forEach(function (asserterName) { if (excludeNames.indexOf(asserterName) !== -1) { return; } var pd = Object.getOwnPropertyDescriptor(ctx, asserterName); Object.defineProperty(chainableMethodWrapper, asserterName, pd); }); } transferFlags(this, chainableMethodWrapper); return proxify(chainableMethodWrapper); } , configurable: true }); };