chai-as-promised
Advanced tools
Comparing version 3.1.0 to 3.2.0
@@ -77,3 +77,3 @@ (function (chaiAsPromised) { | ||
return promiseWithAsserters(transformedPromise, this); | ||
return makeAssertionPromise(transformedPromise, this); | ||
}; | ||
@@ -222,6 +222,6 @@ | ||
return promiseWithAsserters(transformedPromise.then(onTransformedFulfilled, onTransformedRejected), this); | ||
return makeAssertionPromise(transformedPromise.then(onTransformedFulfilled, onTransformedRejected), this); | ||
}.bind(this); | ||
var transformedPromise = promiseWithAsserters(this._obj.then(onOriginalFulfilled, onOriginalRejected), this); | ||
var transformedPromise = makeAssertionPromise(this._obj.then(onOriginalFulfilled, onOriginalRejected), this); | ||
Object.defineProperty(transformedPromise, "with", { enumerable: true, configurable: true, value: withMethod }); | ||
@@ -232,35 +232,48 @@ | ||
function promiseWithAsserters(promise, originalAssertion) { | ||
function makeAssertion(fulfillmentValue) { | ||
var newAssertion = null; | ||
function isChaiAsPromisedAsserter(asserterName) { | ||
return ["fulfilled", "rejected", "broken", "eventually", "become"].indexOf(asserterName) !== -1; | ||
} | ||
if (fulfillmentValue instanceof Assertion) { | ||
newAssertion = new Assertion(fulfillmentValue._obj); | ||
utils.flag(newAssertion, "negate", utils.flag(fulfillmentValue, "negate")); | ||
} else { | ||
newAssertion = new Assertion(fulfillmentValue); | ||
utils.flag(newAssertion, "negate", utils.flag(originalAssertion, "negate")); | ||
} | ||
function makeAssertionPromiseToDoAsserter(currentAssertionPromise, previousAssertionPromise, doAsserter) { | ||
var promiseToDoAsserter = currentAssertionPromise.then(function (fulfillmentValue) { | ||
// The previous assertion promise might have picked up some flags while waiting for fulfillment. | ||
utils.transferFlags(previousAssertionPromise, currentAssertionPromise); | ||
return newAssertion; | ||
} | ||
// Replace the object flag with the fulfillment value, so that doAsserter can operate properly. | ||
utils.flag(currentAssertionPromise, "object", fulfillmentValue); | ||
function promiseToDoAsserter(doAsserter) { | ||
var promiseForAssertion = promise.then(makeAssertion); | ||
var basicPromiseToDoAsserter = promiseForAssertion.then(doAsserter); | ||
var promiseToDoAsserterWithAsserters = promiseWithAsserters(basicPromiseToDoAsserter, originalAssertion); | ||
// Perform the actual asserter action. | ||
doAsserter(currentAssertionPromise); | ||
return promiseToDoAsserterWithAsserters; | ||
} | ||
// Return the fulfillmentValue for future assertions of the new assertion-promise to act upon. | ||
return fulfillmentValue; | ||
}); | ||
return makeAssertionPromise(promiseToDoAsserter, currentAssertionPromise); | ||
} | ||
// Use `Object.create` to ensure we get an extensible promise, so we can add `with`. Q promises, for example, | ||
// are non-extensible. | ||
var augmentedPromise = Object.create(promise); | ||
function makeAssertionPromise(promise, baseAssertion) { | ||
// An assertion-promise is an (extensible!) promise with the following additions: | ||
var assertionPromise = Object.create(promise); | ||
// 1. A `notify` method. | ||
addNotifyMethod(assertionPromise); | ||
// 2. An `assert` method that acts exactly as it would on an assertion. This is called by promisified | ||
// asserters after the promise fulfills. | ||
assertionPromise.assert = function () { | ||
return Assertion.prototype.assert.apply(assertionPromise, arguments); | ||
}; | ||
// 3. Chai asserters, which act upon the promise's fulfillment value. | ||
var asserterNames = Object.getOwnPropertyNames(Assertion.prototype); | ||
asserterNames.forEach(function (asserterName) { | ||
if (["fulfilled", "rejected", "broken", "eventually", "become"].indexOf(asserterName) !== -1) { | ||
Object.defineProperty(augmentedPromise, asserterName, { | ||
get: function () { | ||
throw new Error("Cannot use Chai as Promised asserters more than once in an assertion."); | ||
} | ||
// We already added `notify` and `assert`; don't mess with those. | ||
if (asserterName === "notify" || asserterName === "assert") { | ||
return; | ||
} | ||
// Only add asserters for other libraries; poison-pill Chai as Promised ones. | ||
if (isChaiAsPromisedAsserter(asserterName)) { | ||
utils.addProperty(assertionPromise, asserterName, function () { | ||
throw new Error("Cannot use Chai as Promised asserters more than once in an assertion."); | ||
}); | ||
@@ -270,52 +283,57 @@ return; | ||
// The asserter will need to be added differently depending on its type. In all cases we use | ||
// `makeAssertionPromiseToDoAsserter`, which, given this current `assertionPromise` we are going to | ||
// return, plus the `baseAssertion` we are basing it off of, will return a new assertion-promise that | ||
// builds off of `assertionPromise` and `baseAssertion` to perform the actual asserter action upon | ||
// fulfillment. | ||
var propertyDescriptor = Object.getOwnPropertyDescriptor(Assertion.prototype, asserterName); | ||
if (propertyDescriptor.value) { | ||
propertyDescriptor.value = function () { | ||
if (typeof propertyDescriptor.value === "function") { | ||
// Case 1: simple method asserters | ||
utils.addMethod(assertionPromise, asserterName, function () { | ||
var args = arguments; | ||
return promiseToDoAsserter(function (assertion) { | ||
return assertion[asserterName].apply(assertion, args); | ||
return makeAssertionPromiseToDoAsserter(assertionPromise, baseAssertion, function () { | ||
propertyDescriptor.value.apply(assertionPromise, args); | ||
}); | ||
}; | ||
} else if (propertyDescriptor.get) { | ||
// This case is complicated by asserters that can be used as both chainers and as terminators, e.g. | ||
// (5).should.not.be.an.instanceOf(Object) vs. (5).should.not.be.an("object"). In such cases the getter | ||
// will return a function with its [[Prototype]] set to the assertion, with the function used in the | ||
// terminator case and the assertion [[Prototype]] used in the chainer case. | ||
var getterIsFunction = false; | ||
}); | ||
} else if (typeof propertyDescriptor.get === "function") { | ||
// Case 2: property asserters. These break down into two subcases: chainable methods, and pure | ||
// properties. An example of the former is `a`/`an`: `.should.be.an.instanceOf` vs. | ||
// `should.be.an("object")`. | ||
var isChainableMethod = false; | ||
try { | ||
getterIsFunction = typeof propertyDescriptor.get.call({}) === "function"; | ||
isChainableMethod = typeof propertyDescriptor.get.call({}) === "function"; | ||
} catch (e) { } | ||
if (getterIsFunction) { | ||
// If we've detected we're in the special case described above, we need to handle it specially: | ||
// The getter should be for a function that calls the original function, and the function should | ||
// have its [[Prototype]] set (via `__proto__`, ugh) to the augmented promise, which you can chain | ||
// off of just like an assertion (but better). | ||
propertyDescriptor.get = function () { | ||
/*jshint proto:true */ | ||
function funcToBeGotten() { | ||
if (isChainableMethod) { | ||
// Case 2A: chainable methods. Recreate the chainable method, but operating on the augmented | ||
// promise. We need to copy both the assertion behavior and the chaining behavior, since the | ||
// chaining behavior might for example set flags on the object. | ||
utils.addChainableMethod( | ||
assertionPromise, | ||
asserterName, | ||
function () { | ||
var args = arguments; | ||
return promiseToDoAsserter(function (assertion) { | ||
return Function.prototype.apply.call(assertion[asserterName], assertion, args); | ||
return makeAssertionPromiseToDoAsserter(assertionPromise, baseAssertion, function () { | ||
propertyDescriptor.get().apply(assertionPromise, args); | ||
}); | ||
}, | ||
function () { | ||
propertyDescriptor.get.call(assertionPromise); | ||
} | ||
funcToBeGotten.__proto__ = augmentedPromise; | ||
return funcToBeGotten; | ||
}; | ||
); | ||
} else { | ||
propertyDescriptor.get = function () { | ||
return promiseToDoAsserter(function (assertion) { | ||
return assertion[asserterName]; | ||
// Case 2B: pure property case | ||
utils.addProperty(assertionPromise, asserterName, function () { | ||
return makeAssertionPromiseToDoAsserter(assertionPromise, baseAssertion, function () { | ||
propertyDescriptor.get.call(assertionPromise); | ||
}); | ||
}; | ||
}); | ||
} | ||
} | ||
Object.defineProperty(augmentedPromise, asserterName, propertyDescriptor); | ||
}); | ||
addNotifyMethod(augmentedPromise); | ||
return augmentedPromise; | ||
return assertionPromise; | ||
} | ||
@@ -328,3 +346,3 @@ | ||
property("eventually", function () { | ||
return promiseWithAsserters(this._obj, this); | ||
return makeAssertionPromise(this._obj, this); | ||
}); | ||
@@ -331,0 +349,0 @@ |
@@ -0,0 +0,0 @@ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE |
@@ -9,3 +9,3 @@ { | ||
], | ||
"version": "3.1.0", | ||
"version": "3.2.0", | ||
"author": "Domenic Denicola <domenic@domenicdenicola.com> (http://domenicdenicola.com)", | ||
@@ -37,3 +37,3 @@ "repository": { | ||
"mocha": "1", | ||
"chai": "1", | ||
"chai": ">= 1.0.3", | ||
"cover": "*", | ||
@@ -40,0 +40,0 @@ "jshint": "*", |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
56329
357