chai-as-promised
Advanced tools
Comparing version 7.1.2 to 8.0.0-beta.1
@@ -1,361 +0,453 @@ | ||
"use strict"; | ||
/* eslint-disable no-invalid-this */ | ||
let checkError = require("check-error"); | ||
import * as checkErrorDefault from 'check-error'; | ||
module.exports = (chai, utils) => { | ||
const Assertion = chai.Assertion; | ||
const assert = chai.assert; | ||
const proxify = utils.proxify; | ||
let checkError = checkErrorDefault; | ||
// If we are using a version of Chai that has checkError on it, | ||
// we want to use that version to be consistent. Otherwise, we use | ||
// what was passed to the factory. | ||
if (utils.checkError) { | ||
checkError = utils.checkError; | ||
} | ||
export default function (chai, utils) { | ||
const Assertion = chai.Assertion; | ||
const assert = chai.assert; | ||
const proxify = utils.proxify; | ||
function isLegacyJQueryPromise(thenable) { | ||
// jQuery promises are Promises/A+-compatible since 3.0.0. jQuery 3.0.0 is also the first version | ||
// to define the catch method. | ||
return typeof thenable.catch !== "function" && | ||
typeof thenable.always === "function" && | ||
typeof thenable.done === "function" && | ||
typeof thenable.fail === "function" && | ||
typeof thenable.pipe === "function" && | ||
typeof thenable.progress === "function" && | ||
typeof thenable.state === "function"; | ||
} | ||
// If we are using a version of Chai that has checkError on it, | ||
// we want to use that version to be consistent. Otherwise, we use | ||
// what was passed to the factory. | ||
if (utils.checkError) { | ||
checkError = utils.checkError; | ||
} | ||
function assertIsAboutPromise(assertion) { | ||
if (typeof assertion._obj.then !== "function") { | ||
throw new TypeError(utils.inspect(assertion._obj) + " is not a thenable."); | ||
} | ||
if (isLegacyJQueryPromise(assertion._obj)) { | ||
throw new TypeError("Chai as Promised is incompatible with thenables of jQuery<3.0.0, sorry! Please " + | ||
"upgrade jQuery or use another Promises/A+ compatible library (see " + | ||
"http://promisesaplus.com/)."); | ||
} | ||
} | ||
function isLegacyJQueryPromise(thenable) { | ||
// jQuery promises are Promises/A+-compatible since 3.0.0. jQuery 3.0.0 is also the first version | ||
// to define the catch method. | ||
return ( | ||
typeof thenable.catch !== 'function' && | ||
typeof thenable.always === 'function' && | ||
typeof thenable.done === 'function' && | ||
typeof thenable.fail === 'function' && | ||
typeof thenable.pipe === 'function' && | ||
typeof thenable.progress === 'function' && | ||
typeof thenable.state === 'function' | ||
); | ||
} | ||
function proxifyIfSupported(assertion) { | ||
return proxify === undefined ? assertion : proxify(assertion); | ||
function assertIsAboutPromise(assertion) { | ||
if (typeof assertion._obj.then !== 'function') { | ||
throw new TypeError( | ||
utils.inspect(assertion._obj) + ' is not a thenable.' | ||
); | ||
} | ||
function method(name, asserter) { | ||
utils.addMethod(Assertion.prototype, name, function () { | ||
assertIsAboutPromise(this); | ||
return asserter.apply(this, arguments); | ||
}); | ||
if (isLegacyJQueryPromise(assertion._obj)) { | ||
throw new TypeError( | ||
'Chai as Promised is incompatible with thenables of jQuery<3.0.0, sorry! Please ' + | ||
'upgrade jQuery or use another Promises/A+ compatible library (see ' + | ||
'http://promisesaplus.com/).' | ||
); | ||
} | ||
} | ||
function property(name, asserter) { | ||
utils.addProperty(Assertion.prototype, name, function () { | ||
assertIsAboutPromise(this); | ||
return proxifyIfSupported(asserter.apply(this, arguments)); | ||
}); | ||
} | ||
function proxifyIfSupported(assertion) { | ||
return proxify === undefined ? assertion : proxify(assertion); | ||
} | ||
function doNotify(promise, done) { | ||
promise.then(() => done(), done); | ||
} | ||
function method(name, asserter) { | ||
utils.addMethod(Assertion.prototype, name, function () { | ||
assertIsAboutPromise(this); | ||
return asserter.apply(this, arguments); | ||
}); | ||
} | ||
// These are for clarity and to bypass Chai refusing to allow `undefined` as actual when used with `assert`. | ||
function assertIfNegated(assertion, message, extra) { | ||
assertion.assert(true, null, message, extra.expected, extra.actual); | ||
} | ||
function property(name, asserter) { | ||
utils.addProperty(Assertion.prototype, name, function () { | ||
assertIsAboutPromise(this); | ||
return proxifyIfSupported(asserter.apply(this, arguments)); | ||
}); | ||
} | ||
function assertIfNotNegated(assertion, message, extra) { | ||
assertion.assert(false, message, null, extra.expected, extra.actual); | ||
} | ||
function doNotify(promise, done) { | ||
promise.then(() => done(), done); | ||
} | ||
function getBasePromise(assertion) { | ||
// We need to chain subsequent asserters on top of ones in the chain already (consider | ||
// `eventually.have.property("foo").that.equals("bar")`), only running them after the existing ones pass. | ||
// So the first base-promise is `assertion._obj`, but after that we use the assertions themselves, i.e. | ||
// previously derived promises, to chain off of. | ||
return typeof assertion.then === "function" ? assertion : assertion._obj; | ||
} | ||
// These are for clarity and to bypass Chai refusing to allow `undefined` as actual when used with `assert`. | ||
function assertIfNegated(assertion, message, extra) { | ||
assertion.assert(true, null, message, extra.expected, extra.actual); | ||
} | ||
function getReasonName(reason) { | ||
return reason instanceof Error ? reason.toString() : checkError.getConstructorName(reason); | ||
} | ||
function assertIfNotNegated(assertion, message, extra) { | ||
assertion.assert(false, message, null, extra.expected, extra.actual); | ||
} | ||
// Grab these first, before we modify `Assertion.prototype`. | ||
function getBasePromise(assertion) { | ||
// We need to chain subsequent asserters on top of ones in the chain already (consider | ||
// `eventually.have.property("foo").that.equals("bar")`), only running them after the existing ones pass. | ||
// So the first base-promise is `assertion._obj`, but after that we use the assertions themselves, i.e. | ||
// previously derived promises, to chain off of. | ||
return typeof assertion.then === 'function' ? assertion : assertion._obj; | ||
} | ||
const propertyNames = Object.getOwnPropertyNames(Assertion.prototype); | ||
function getReasonName(reason) { | ||
return reason instanceof Error | ||
? reason.toString() | ||
: checkError.getConstructorName(reason); | ||
} | ||
const propertyDescs = {}; | ||
for (const name of propertyNames) { | ||
propertyDescs[name] = Object.getOwnPropertyDescriptor(Assertion.prototype, name); | ||
} | ||
// Grab these first, before we modify `Assertion.prototype`. | ||
property("fulfilled", function () { | ||
const derivedPromise = getBasePromise(this).then( | ||
value => { | ||
assertIfNegated(this, | ||
"expected promise not to be fulfilled but it was fulfilled with #{act}", | ||
{ actual: value }); | ||
return value; | ||
}, | ||
reason => { | ||
assertIfNotNegated(this, | ||
"expected promise to be fulfilled but it was rejected with #{act}", | ||
{ actual: getReasonName(reason) }); | ||
return reason; | ||
} | ||
const propertyNames = Object.getOwnPropertyNames(Assertion.prototype); | ||
const propertyDescs = {}; | ||
for (const name of propertyNames) { | ||
propertyDescs[name] = Object.getOwnPropertyDescriptor( | ||
Assertion.prototype, | ||
name | ||
); | ||
} | ||
property('fulfilled', function () { | ||
const derivedPromise = getBasePromise(this).then( | ||
(value) => { | ||
assertIfNegated( | ||
this, | ||
'expected promise not to be fulfilled but it was fulfilled with #{act}', | ||
{actual: value} | ||
); | ||
return value; | ||
}, | ||
(reason) => { | ||
assertIfNotNegated( | ||
this, | ||
'expected promise to be fulfilled but it was rejected with #{act}', | ||
{actual: getReasonName(reason)} | ||
); | ||
return reason; | ||
} | ||
); | ||
module.exports.transferPromiseness(this, derivedPromise); | ||
return this; | ||
}); | ||
transferPromiseness(this, derivedPromise); | ||
return this; | ||
}); | ||
property("rejected", function () { | ||
const derivedPromise = getBasePromise(this).then( | ||
value => { | ||
assertIfNotNegated(this, | ||
"expected promise to be rejected but it was fulfilled with #{act}", | ||
{ actual: value }); | ||
return value; | ||
}, | ||
reason => { | ||
assertIfNegated(this, | ||
"expected promise not to be rejected but it was rejected with #{act}", | ||
{ actual: getReasonName(reason) }); | ||
// Return the reason, transforming this into a fulfillment, to allow further assertions, e.g. | ||
// `promise.should.be.rejected.and.eventually.equal("reason")`. | ||
return reason; | ||
} | ||
property('rejected', function () { | ||
const derivedPromise = getBasePromise(this).then( | ||
(value) => { | ||
assertIfNotNegated( | ||
this, | ||
'expected promise to be rejected but it was fulfilled with #{act}', | ||
{actual: value} | ||
); | ||
return value; | ||
}, | ||
(reason) => { | ||
assertIfNegated( | ||
this, | ||
'expected promise not to be rejected but it was rejected with #{act}', | ||
{actual: getReasonName(reason)} | ||
); | ||
module.exports.transferPromiseness(this, derivedPromise); | ||
return this; | ||
}); | ||
// Return the reason, transforming this into a fulfillment, to allow further assertions, e.g. | ||
// `promise.should.be.rejected.and.eventually.equal("reason")`. | ||
return reason; | ||
} | ||
); | ||
method("rejectedWith", function (errorLike, errMsgMatcher, message) { | ||
let errorLikeName = null; | ||
const negate = utils.flag(this, "negate") || false; | ||
transferPromiseness(this, derivedPromise); | ||
return this; | ||
}); | ||
// rejectedWith with that is called without arguments is | ||
// the same as a plain ".rejected" use. | ||
if (errorLike === undefined && errMsgMatcher === undefined && | ||
message === undefined) { | ||
/* eslint-disable no-unused-expressions */ | ||
return this.rejected; | ||
/* eslint-enable no-unused-expressions */ | ||
} | ||
method('rejectedWith', function (errorLike, errMsgMatcher, message) { | ||
let errorLikeName = null; | ||
const negate = utils.flag(this, 'negate') || false; | ||
if (message !== undefined) { | ||
utils.flag(this, "message", message); | ||
// rejectedWith with that is called without arguments is | ||
// the same as a plain ".rejected" use. | ||
if ( | ||
errorLike === undefined && | ||
errMsgMatcher === undefined && | ||
message === undefined | ||
) { | ||
/* eslint-disable no-unused-expressions */ | ||
return this.rejected; | ||
/* eslint-enable no-unused-expressions */ | ||
} | ||
if (message !== undefined) { | ||
utils.flag(this, 'message', message); | ||
} | ||
if (errorLike instanceof RegExp || typeof errorLike === 'string') { | ||
errMsgMatcher = errorLike; | ||
errorLike = null; | ||
} else if (errorLike && errorLike instanceof Error) { | ||
errorLikeName = errorLike.toString(); | ||
} else if (typeof errorLike === 'function') { | ||
errorLikeName = checkError.getConstructorName(errorLike); | ||
} else { | ||
errorLike = null; | ||
} | ||
const everyArgIsDefined = Boolean(errorLike && errMsgMatcher); | ||
let matcherRelation = 'including'; | ||
if (errMsgMatcher instanceof RegExp) { | ||
matcherRelation = 'matching'; | ||
} | ||
const derivedPromise = getBasePromise(this).then( | ||
(value) => { | ||
let assertionMessage = null; | ||
let expected = null; | ||
if (errorLike) { | ||
assertionMessage = | ||
'expected promise to be rejected with #{exp} but it was fulfilled with #{act}'; | ||
expected = errorLikeName; | ||
} else if (errMsgMatcher) { | ||
assertionMessage = | ||
`expected promise to be rejected with an error ${matcherRelation} #{exp} but ` + | ||
`it was fulfilled with #{act}`; | ||
expected = errMsgMatcher; | ||
} | ||
if (errorLike instanceof RegExp || typeof errorLike === "string") { | ||
errMsgMatcher = errorLike; | ||
errorLike = null; | ||
} else if (errorLike && errorLike instanceof Error) { | ||
errorLikeName = errorLike.toString(); | ||
} else if (typeof errorLike === "function") { | ||
errorLikeName = checkError.getConstructorName(errorLike); | ||
assertIfNotNegated(this, assertionMessage, {expected, actual: value}); | ||
return value; | ||
}, | ||
(reason) => { | ||
const errorLikeCompatible = | ||
errorLike && | ||
(errorLike instanceof Error | ||
? checkError.compatibleInstance(reason, errorLike) | ||
: checkError.compatibleConstructor(reason, errorLike)); | ||
const errMsgMatcherCompatible = | ||
errMsgMatcher === reason || | ||
(errMsgMatcher && | ||
reason && | ||
checkError.compatibleMessage(reason, errMsgMatcher)); | ||
const reasonName = getReasonName(reason); | ||
if (negate && everyArgIsDefined) { | ||
if (errorLikeCompatible && errMsgMatcherCompatible) { | ||
this.assert( | ||
true, | ||
null, | ||
'expected promise not to be rejected with #{exp} but it was rejected ' + | ||
'with #{act}', | ||
errorLikeName, | ||
reasonName | ||
); | ||
} | ||
} else { | ||
errorLike = null; | ||
} | ||
const everyArgIsDefined = Boolean(errorLike && errMsgMatcher); | ||
if (errorLike) { | ||
this.assert( | ||
errorLikeCompatible, | ||
'expected promise to be rejected with #{exp} but it was rejected with #{act}', | ||
'expected promise not to be rejected with #{exp} but it was rejected ' + | ||
'with #{act}', | ||
errorLikeName, | ||
reasonName | ||
); | ||
} | ||
let matcherRelation = "including"; | ||
if (errMsgMatcher instanceof RegExp) { | ||
matcherRelation = "matching"; | ||
if (errMsgMatcher) { | ||
this.assert( | ||
errMsgMatcherCompatible, | ||
`expected promise to be rejected with an error ${matcherRelation} #{exp} but got ` + | ||
`#{act}`, | ||
`expected promise not to be rejected with an error ${matcherRelation} #{exp}`, | ||
errMsgMatcher, | ||
checkError.getMessage(reason) | ||
); | ||
} | ||
} | ||
const derivedPromise = getBasePromise(this).then( | ||
value => { | ||
let assertionMessage = null; | ||
let expected = null; | ||
return reason; | ||
} | ||
); | ||
if (errorLike) { | ||
assertionMessage = "expected promise to be rejected with #{exp} but it was fulfilled with #{act}"; | ||
expected = errorLikeName; | ||
} else if (errMsgMatcher) { | ||
assertionMessage = `expected promise to be rejected with an error ${matcherRelation} #{exp} but ` + | ||
`it was fulfilled with #{act}`; | ||
expected = errMsgMatcher; | ||
} | ||
transferPromiseness(this, derivedPromise); | ||
return this; | ||
}); | ||
assertIfNotNegated(this, assertionMessage, { expected, actual: value }); | ||
return value; | ||
}, | ||
reason => { | ||
const errorLikeCompatible = errorLike && (errorLike instanceof Error ? | ||
checkError.compatibleInstance(reason, errorLike) : | ||
checkError.compatibleConstructor(reason, errorLike)); | ||
property('eventually', function () { | ||
utils.flag(this, 'eventually', true); | ||
return this; | ||
}); | ||
const errMsgMatcherCompatible = errMsgMatcher && checkError.compatibleMessage(reason, errMsgMatcher); | ||
method('notify', function (done) { | ||
doNotify(getBasePromise(this), done); | ||
return this; | ||
}); | ||
const reasonName = getReasonName(reason); | ||
method('become', function (value, message) { | ||
return this.eventually.deep.equal(value, message); | ||
}); | ||
if (negate && everyArgIsDefined) { | ||
if (errorLikeCompatible && errMsgMatcherCompatible) { | ||
this.assert(true, | ||
null, | ||
"expected promise not to be rejected with #{exp} but it was rejected " + | ||
"with #{act}", | ||
errorLikeName, | ||
reasonName); | ||
} | ||
} else { | ||
if (errorLike) { | ||
this.assert(errorLikeCompatible, | ||
"expected promise to be rejected with #{exp} but it was rejected with #{act}", | ||
"expected promise not to be rejected with #{exp} but it was rejected " + | ||
"with #{act}", | ||
errorLikeName, | ||
reasonName); | ||
} | ||
// ### `eventually` | ||
if (errMsgMatcher) { | ||
this.assert(errMsgMatcherCompatible, | ||
`expected promise to be rejected with an error ${matcherRelation} #{exp} but got ` + | ||
`#{act}`, | ||
`expected promise not to be rejected with an error ${matcherRelation} #{exp}`, | ||
errMsgMatcher, | ||
checkError.getMessage(reason)); | ||
} | ||
} | ||
// We need to be careful not to trigger any getters, thus `Object.getOwnPropertyDescriptor` usage. | ||
const methodNames = propertyNames.filter((name) => { | ||
return name !== 'assert' && typeof propertyDescs[name].value === 'function'; | ||
}); | ||
return reason; | ||
} | ||
); | ||
methodNames.forEach((methodName) => { | ||
Assertion.overwriteMethod( | ||
methodName, | ||
(originalMethod) => | ||
function () { | ||
return doAsserterAsyncAndAddThen(originalMethod, this, arguments); | ||
} | ||
); | ||
}); | ||
module.exports.transferPromiseness(this, derivedPromise); | ||
return this; | ||
}); | ||
const getterNames = propertyNames.filter((name) => { | ||
return name !== '_obj' && typeof propertyDescs[name].get === 'function'; | ||
}); | ||
property("eventually", function () { | ||
utils.flag(this, "eventually", true); | ||
return this; | ||
}); | ||
getterNames.forEach((getterName) => { | ||
// Chainable methods are things like `an`, which can work both for `.should.be.an.instanceOf` and as | ||
// `should.be.an("object")`. We need to handle those specially. | ||
const isChainableMethod = Object.prototype.hasOwnProperty.call( | ||
Assertion.prototype.__methods, | ||
getterName | ||
); | ||
method("notify", function (done) { | ||
doNotify(getBasePromise(this), done); | ||
return this; | ||
}); | ||
if (isChainableMethod) { | ||
Assertion.overwriteChainableMethod( | ||
getterName, | ||
(originalMethod) => | ||
function () { | ||
return doAsserterAsyncAndAddThen(originalMethod, this, arguments); | ||
}, | ||
(originalGetter) => | ||
function () { | ||
return doAsserterAsyncAndAddThen(originalGetter, this); | ||
} | ||
); | ||
} else { | ||
Assertion.overwriteProperty( | ||
getterName, | ||
(originalGetter) => | ||
function () { | ||
return proxifyIfSupported( | ||
doAsserterAsyncAndAddThen(originalGetter, this) | ||
); | ||
} | ||
); | ||
} | ||
}); | ||
method("become", function (value, message) { | ||
return this.eventually.deep.equal(value, message); | ||
}); | ||
function doAsserterAsyncAndAddThen(asserter, assertion, args) { | ||
// Since we're intercepting all methods/properties, we need to just pass through if they don't want | ||
// `eventually`, or if we've already fulfilled the promise (see below). | ||
if (!utils.flag(assertion, 'eventually')) { | ||
asserter.apply(assertion, args); | ||
return assertion; | ||
} | ||
// ### `eventually` | ||
const derivedPromise = getBasePromise(assertion) | ||
.then((value) => { | ||
// Set up the environment for the asserter to actually run: `_obj` should be the fulfillment value, and | ||
// now that we have the value, we're no longer in "eventually" mode, so we won't run any of this code, | ||
// just the base Chai code that we get to via the short-circuit above. | ||
assertion._obj = value; | ||
utils.flag(assertion, 'eventually', false); | ||
// We need to be careful not to trigger any getters, thus `Object.getOwnPropertyDescriptor` usage. | ||
const methodNames = propertyNames.filter(name => { | ||
return name !== "assert" && typeof propertyDescs[name].value === "function"; | ||
}); | ||
return args ? transformAsserterArgs(args) : args; | ||
}) | ||
.then((newArgs) => { | ||
asserter.apply(assertion, newArgs); | ||
methodNames.forEach(methodName => { | ||
Assertion.overwriteMethod(methodName, originalMethod => function () { | ||
return doAsserterAsyncAndAddThen(originalMethod, this, arguments); | ||
}); | ||
}); | ||
// Because asserters, for example `property`, can change the value of `_obj` (i.e. change the "object" | ||
// flag), we need to communicate this value change to subsequent chained asserters. Since we build a | ||
// promise chain paralleling the asserter chain, we can use it to communicate such changes. | ||
return assertion._obj; | ||
}); | ||
const getterNames = propertyNames.filter(name => { | ||
return name !== "_obj" && typeof propertyDescs[name].get === "function"; | ||
}); | ||
transferPromiseness(assertion, derivedPromise); | ||
return assertion; | ||
} | ||
getterNames.forEach(getterName => { | ||
// Chainable methods are things like `an`, which can work both for `.should.be.an.instanceOf` and as | ||
// `should.be.an("object")`. We need to handle those specially. | ||
const isChainableMethod = Assertion.prototype.__methods.hasOwnProperty(getterName); | ||
// ### Now use the `Assertion` framework to build an `assert` interface. | ||
const originalAssertMethods = Object.getOwnPropertyNames(assert).filter( | ||
(propName) => { | ||
return typeof assert[propName] === 'function'; | ||
} | ||
); | ||
if (isChainableMethod) { | ||
Assertion.overwriteChainableMethod( | ||
getterName, | ||
originalMethod => function () { | ||
return doAsserterAsyncAndAddThen(originalMethod, this, arguments); | ||
}, | ||
originalGetter => function () { | ||
return doAsserterAsyncAndAddThen(originalGetter, this); | ||
} | ||
); | ||
} else { | ||
Assertion.overwriteProperty(getterName, originalGetter => function () { | ||
return proxifyIfSupported(doAsserterAsyncAndAddThen(originalGetter, this)); | ||
}); | ||
} | ||
}); | ||
assert.isFulfilled = (promise, message) => | ||
new Assertion(promise, message).to.be.fulfilled; | ||
function doAsserterAsyncAndAddThen(asserter, assertion, args) { | ||
// Since we're intercepting all methods/properties, we need to just pass through if they don't want | ||
// `eventually`, or if we've already fulfilled the promise (see below). | ||
if (!utils.flag(assertion, "eventually")) { | ||
asserter.apply(assertion, args); | ||
return assertion; | ||
} | ||
assert.isRejected = (promise, errorLike, errMsgMatcher, message) => { | ||
const assertion = new Assertion(promise, message); | ||
return assertion.to.be.rejectedWith(errorLike, errMsgMatcher, message); | ||
}; | ||
const derivedPromise = getBasePromise(assertion).then(value => { | ||
// Set up the environment for the asserter to actually run: `_obj` should be the fulfillment value, and | ||
// now that we have the value, we're no longer in "eventually" mode, so we won't run any of this code, | ||
// just the base Chai code that we get to via the short-circuit above. | ||
assertion._obj = value; | ||
utils.flag(assertion, "eventually", false); | ||
assert.becomes = (promise, value, message) => | ||
assert.eventually.deepEqual(promise, value, message); | ||
return args ? module.exports.transformAsserterArgs(args) : args; | ||
}).then(newArgs => { | ||
asserter.apply(assertion, newArgs); | ||
assert.doesNotBecome = (promise, value, message) => | ||
assert.eventually.notDeepEqual(promise, value, message); | ||
// Because asserters, for example `property`, can change the value of `_obj` (i.e. change the "object" | ||
// flag), we need to communicate this value change to subsequent chained asserters. Since we build a | ||
// promise chain paralleling the asserter chain, we can use it to communicate such changes. | ||
return assertion._obj; | ||
}); | ||
assert.eventually = {}; | ||
originalAssertMethods.forEach((assertMethodName) => { | ||
assert.eventually[assertMethodName] = function (promise) { | ||
const otherArgs = Array.prototype.slice.call(arguments, 1); | ||
module.exports.transferPromiseness(assertion, derivedPromise); | ||
return assertion; | ||
} | ||
let customRejectionHandler; | ||
const message = arguments[assert[assertMethodName].length - 1]; | ||
if (typeof message === 'string') { | ||
customRejectionHandler = (reason) => { | ||
throw new chai.AssertionError( | ||
`${message}\n\nOriginal reason: ${utils.inspect(reason)}` | ||
); | ||
}; | ||
} | ||
// ### Now use the `Assertion` framework to build an `assert` interface. | ||
const originalAssertMethods = Object.getOwnPropertyNames(assert).filter(propName => { | ||
return typeof assert[propName] === "function"; | ||
}); | ||
const returnedPromise = promise.then( | ||
(fulfillmentValue) => | ||
assert[assertMethodName].apply( | ||
assert, | ||
[fulfillmentValue].concat(otherArgs) | ||
), | ||
customRejectionHandler | ||
); | ||
assert.isFulfilled = (promise, message) => (new Assertion(promise, message)).to.be.fulfilled; | ||
returnedPromise.notify = (done) => { | ||
doNotify(returnedPromise, done); | ||
}; | ||
assert.isRejected = (promise, errorLike, errMsgMatcher, message) => { | ||
const assertion = new Assertion(promise, message); | ||
return assertion.to.be.rejectedWith(errorLike, errMsgMatcher, message); | ||
return returnedPromise; | ||
}; | ||
}); | ||
} | ||
assert.becomes = (promise, value, message) => assert.eventually.deepEqual(promise, value, message); | ||
function defaultTransferPromiseness(assertion, promise) { | ||
assertion.then = promise.then.bind(promise); | ||
} | ||
assert.doesNotBecome = (promise, value, message) => assert.eventually.notDeepEqual(promise, value, message); | ||
function defaultTransformAsserterArgs(values) { | ||
return values; | ||
} | ||
assert.eventually = {}; | ||
originalAssertMethods.forEach(assertMethodName => { | ||
assert.eventually[assertMethodName] = function (promise) { | ||
const otherArgs = Array.prototype.slice.call(arguments, 1); | ||
let customTransferPromiseness; | ||
let customTransformAsserterArgs; | ||
let customRejectionHandler; | ||
const message = arguments[assert[assertMethodName].length - 1]; | ||
if (typeof message === "string") { | ||
customRejectionHandler = reason => { | ||
throw new chai.AssertionError(`${message}\n\nOriginal reason: ${utils.inspect(reason)}`); | ||
}; | ||
} | ||
export function setTransferPromiseness(fn) { | ||
customTransferPromiseness = fn || defaultTransferPromiseness; | ||
} | ||
const returnedPromise = promise.then( | ||
fulfillmentValue => assert[assertMethodName].apply(assert, [fulfillmentValue].concat(otherArgs)), | ||
customRejectionHandler | ||
); | ||
export function setTransformAsserterArgs(fn) { | ||
customTransformAsserterArgs = fn || defaultTransformAsserterArgs; | ||
} | ||
returnedPromise.notify = done => { | ||
doNotify(returnedPromise, done); | ||
}; | ||
export function transferPromiseness(assertion, promise) { | ||
if (customTransferPromiseness) { | ||
customTransferPromiseness(assertion, promise); | ||
} else { | ||
defaultTransferPromiseness(assertion, promise); | ||
} | ||
} | ||
return returnedPromise; | ||
}; | ||
}); | ||
}; | ||
module.exports.transferPromiseness = (assertion, promise) => { | ||
assertion.then = promise.then.bind(promise); | ||
}; | ||
module.exports.transformAsserterArgs = values => values; | ||
export function transformAsserterArgs(values) { | ||
if (customTransformAsserterArgs) { | ||
return customTransformAsserterArgs(values); | ||
} | ||
return defaultTransformAsserterArgs(values); | ||
} |
{ | ||
"name": "chai-as-promised", | ||
"type": "module", | ||
"description": "Extends Chai with assertions about promises.", | ||
@@ -14,7 +15,13 @@ "keywords": [ | ||
], | ||
"version": "7.1.2", | ||
"version": "8.0.0-beta.1", | ||
"author": "Domenic Denicola <d@domenic.me> (https://domenic.me)", | ||
"license": "WTFPL", | ||
"repository": "domenic/chai-as-promised", | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/chaijs/chai-as-promised.git" | ||
}, | ||
"main": "./lib/chai-as-promised.js", | ||
"exports": { | ||
".": "./lib/chai-as-promised.js" | ||
}, | ||
"files": [ | ||
@@ -24,9 +31,8 @@ "lib" | ||
"scripts": { | ||
"test": "mocha", | ||
"test-travis": "npm install chai@$CHAI_VERSION && npm test", | ||
"test": "c8 mocha", | ||
"lint": "eslint .", | ||
"cover": "istanbul cover node_modules/mocha/bin/_mocha && opener ./coverage/lcov-report/lib/chai-as-promised.js.html" | ||
"format": "prettier --write lib test" | ||
}, | ||
"dependencies": { | ||
"check-error": "^1.0.2" | ||
"check-error": "^2.0.0" | ||
}, | ||
@@ -37,7 +43,8 @@ "peerDependencies": { | ||
"devDependencies": { | ||
"chai": "^4.0.2", | ||
"eslint": "^3.19.0", | ||
"istanbul": "0.4.5", | ||
"mocha": "^3.4.2" | ||
"c8": "^9.1.0", | ||
"chai": "^5.1.0", | ||
"eslint": "^8.57.0", | ||
"mocha": "^10.4.0", | ||
"prettier": "^3.2.5" | ||
} | ||
} |
@@ -110,7 +110,9 @@ <a href="http://promisesaplus.com/"> | ||
```js | ||
chaiAsPromised.transferPromiseness = function (assertion, promise) { | ||
import {setTransferPromiseness} from 'chai-as-promised'; | ||
setTransferPromiseness(function (assertion, promise) { | ||
assertion.then = promise.then.bind(promise); // this is all you get by default | ||
assertion.finally = promise.finally.bind(promise); | ||
assertion.done = promise.done.bind(promise); | ||
}; | ||
}); | ||
``` | ||
@@ -123,5 +125,7 @@ | ||
```js | ||
chaiAsPromised.transformAsserterArgs = function (args) { | ||
import {transformAsserterArgs} from 'chai-as-promised'; | ||
setTransformAsserterArgs(function (args) { | ||
return args.map(function (x) { return x + 1; }); | ||
} | ||
}); | ||
@@ -138,5 +142,5 @@ Promise.resolve(2).should.eventually.equal(2); // will now fail! | ||
chaiAsPromised.transformAsserterArgs = function (args) { | ||
setTransformAsserterArgs(function (args) { | ||
return Promise.all(args); | ||
}; | ||
}); | ||
@@ -219,4 +223,4 @@ // But now it will pass, since we transformed the array of promises for numbers into | ||
```javascript | ||
var chai = require("chai"); | ||
var chaiAsPromised = require("chai-as-promised"); | ||
import * as chai from 'chai'; | ||
import chaiAsPromised from 'chai-as-promised'; | ||
@@ -226,5 +230,5 @@ chai.use(chaiAsPromised); | ||
// Then either: | ||
var expect = chai.expect; | ||
const expect = chai.expect; | ||
// or: | ||
var assert = chai.assert; | ||
const assert = chai.assert; | ||
// or: | ||
@@ -239,6 +243,2 @@ chai.should(); | ||
### In the Browser | ||
To use Chai as Promised in environments that don't support Node.js-like CommonJS modules, you'll need to use a bundling tool like [browserify](http://browserify.org/). See also the note below about browser compatibility. | ||
### Karma | ||
@@ -250,2 +250,4 @@ | ||
Chai as Promised requires Node v4+ or a browser with equivalent support for modern JavaScript syntax. If your browser doesn't support modern JavaScript syntax, you'll need to transpile it down using a tool like [Babel](http://babeljs.io/). | ||
Chai as Promised requires support for ES modules and modern JavaScript syntax. | ||
If your browser doesn't support this, you will need to transpile it down using | ||
a tool like [Babel](https://babeljs.io/). |
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
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
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
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
387
247
0
0
Yes
27483
5
2
- Removedcheck-error@1.0.3(transitive)
- Removedget-func-name@2.0.2(transitive)
Updatedcheck-error@^2.0.0