sinon-chai
Version:
Extends Chai with assertions for the Sinon.JS mocking framework.
196 lines (176 loc) • 5.31 kB
JavaScript
export default function (chai, utils) {
const slice = Array.prototype.slice;
function isSpy(putativeSpy) {
return (
typeof putativeSpy === 'function' &&
typeof putativeSpy.getCall === 'function' &&
typeof putativeSpy.calledWithExactly === 'function'
);
}
function timesInWords(count) {
switch (count) {
case 1: {
return 'once';
}
case 2: {
return 'twice';
}
case 3: {
return 'thrice';
}
default: {
return (count || 0) + ' times';
}
}
}
function isCall(putativeCall) {
return putativeCall && isSpy(putativeCall.proxy);
}
function assertCanWorkWith(assertion) {
if (!isSpy(assertion._obj) && !isCall(assertion._obj)) {
throw new TypeError(
utils.inspect(assertion._obj) + ' is not a spy or a call to a spy!'
);
}
}
function getMessages(spy, action, nonNegatedSuffix, always, args) {
var verbPhrase = always ? 'always have ' : 'have ';
nonNegatedSuffix = nonNegatedSuffix || '';
if (isSpy(spy.proxy)) {
spy = spy.proxy;
}
function printfArray(array) {
return spy.printf.apply(spy, array);
}
return {
affirmative: function () {
return printfArray(
['expected %n to ' + verbPhrase + action + nonNegatedSuffix].concat(
args
)
);
},
negative: function () {
return printfArray(
['expected %n to not ' + verbPhrase + action].concat(args)
);
}
};
}
function sinonProperty(name, action, nonNegatedSuffix) {
utils.addProperty(chai.Assertion.prototype, name, function () {
assertCanWorkWith(this);
var messages = getMessages(this._obj, action, nonNegatedSuffix, false);
this.assert(this._obj[name], messages.affirmative, messages.negative);
});
}
function sinonPropertyAsBooleanMethod(name, action, nonNegatedSuffix) {
utils.addMethod(chai.Assertion.prototype, name, function (arg) {
assertCanWorkWith(this);
var messages = getMessages(this._obj, action, nonNegatedSuffix, false, [
timesInWords(arg)
]);
this.assert(
this._obj[name] === arg,
messages.affirmative,
messages.negative
);
});
}
function createSinonMethodHandler(sinonName, action, nonNegatedSuffix) {
return function () {
assertCanWorkWith(this);
var alwaysSinonMethod =
'always' + sinonName[0].toUpperCase() + sinonName.substring(1);
var shouldBeAlways =
utils.flag(this, 'always') &&
typeof this._obj[alwaysSinonMethod] === 'function';
var sinonMethodName = shouldBeAlways ? alwaysSinonMethod : sinonName;
var messages = getMessages(
this._obj,
action,
nonNegatedSuffix,
shouldBeAlways,
slice.call(arguments)
);
this.assert(
this._obj[sinonMethodName].apply(this._obj, arguments),
messages.affirmative,
messages.negative
);
};
}
function sinonMethodAsProperty(name, action, nonNegatedSuffix) {
var handler = createSinonMethodHandler(name, action, nonNegatedSuffix);
utils.addProperty(chai.Assertion.prototype, name, handler);
}
function exceptionalSinonMethod(
chaiName,
sinonName,
action,
nonNegatedSuffix
) {
var handler = createSinonMethodHandler(sinonName, action, nonNegatedSuffix);
utils.addMethod(chai.Assertion.prototype, chaiName, handler);
}
function sinonMethod(name, action, nonNegatedSuffix) {
exceptionalSinonMethod(name, name, action, nonNegatedSuffix);
}
utils.addProperty(chai.Assertion.prototype, 'always', function () {
utils.flag(this, 'always', true);
});
sinonProperty(
'called',
'been called',
' at least once, but it was never called'
);
sinonPropertyAsBooleanMethod(
'callCount',
'been called exactly %1',
', but it was called %c%C'
);
sinonProperty(
'calledOnce',
'been called exactly once',
', but it was called %c%C'
);
sinonProperty(
'calledTwice',
'been called exactly twice',
', but it was called %c%C'
);
sinonProperty(
'calledThrice',
'been called exactly thrice',
', but it was called %c%C'
);
sinonMethodAsProperty('calledWithNew', 'been called with new');
sinonMethod('calledBefore', 'been called before %1');
sinonMethod('calledAfter', 'been called after %1');
sinonMethod('calledImmediatelyBefore', 'been called immediately before %1');
sinonMethod('calledImmediatelyAfter', 'been called immediately after %1');
sinonMethod(
'calledOn',
'been called with %1 as this',
', but it was called with %t instead'
);
sinonMethod('calledWith', 'been called with arguments %*', '%D');
sinonMethod(
'calledOnceWith',
'been called exactly once with arguments %*',
'%D'
);
sinonMethod('calledWithExactly', 'been called with exact arguments %*', '%D');
sinonMethod(
'calledOnceWithExactly',
'been called exactly once with exact arguments %*',
'%D'
);
sinonMethod(
'calledWithMatch',
'been called with arguments matching %*',
'%D'
);
sinonMethod('returned', 'returned %1');
exceptionalSinonMethod('thrown', 'threw', 'thrown %1');
}