@sinonjs/referee-sinon
Version:
Sinon.JS assertions for referee
386 lines (342 loc) • 11.7 kB
JavaScript
"use strict";
var slice = require("@sinonjs/commons").prototypes.array.slice;
var calledInOrder = require("@sinonjs/commons").calledInOrder;
var orderByFirstCall = require("@sinonjs/commons").orderByFirstCall;
var referee = require("@sinonjs/referee");
var formatio = require("@sinonjs/formatio");
var sinon = require("sinon");
var timesInWords = [null, "once", "twice", "thrice"];
var push = Array.prototype.push;
function callCount(fake) {
var count = fake.callCount;
return timesInWords[count] || (count || 0) + " times";
}
function callsToString(fake) {
if (typeof fake.getCalls !== "function") {
return "";
}
var calls = fake.getCalls();
if (calls.length === 0) {
return "";
}
return (
"\n" +
calls
.map(function(call) {
return " " + call.toString();
})
.join("\n")
);
}
sinon.expectation.pass = function(assertion) {
referee.emit("pass", assertion);
};
sinon.expectation.fail = function(message) {
referee.fail(message);
};
// Lazy bind the format method to referee's. This way, Sinon will
// always format objects like referee does, even if referee is
// configured after referee-sinon is loaded
var formatter = formatio.configure({
quoteStrings: false,
limitChildrenCount: 250
});
function format() {
return formatter.ascii.apply(formatter, arguments);
}
referee.setFormatter(format);
sinon.setFormatter(format);
function verifyFakes() {
var method, isNot, i, l;
for (i = 0, l = arguments.length; i < l; ++i) {
method = arguments[i];
isNot = (method || "fake") + " is not ";
if (!method) {
this.fail(isNot + "a spy");
}
if (typeof method.calledWith !== "function") {
this.fail(isNot + "stubbed");
}
}
return true;
}
referee.add("callCount", {
assert: function(spy, count) {
verifyFakes.call(this, spy);
return spy.callCount === count;
},
assertMessage:
"Expected ${spyObj} to be called exactly ${expectedTimes} times, but was called ${actualTimes}",
refuteMessage:
"Expected ${spyObj} to not be called exactly ${expectedTimes} times",
expectation: "toHaveCallCount",
values: function(spyObj, count) {
return {
spyObj: spyObj,
actualTimes: callCount(spyObj),
expectedTimes: count
};
}
});
referee.add("called", {
assert: function(spy) {
verifyFakes.call(this, spy);
return spy.called;
},
assertMessage:
"Expected ${spyObj} to be called at least once but was never called",
refuteMessage:
"Expected ${spyObj} to not be called but was called ${times}${calls}",
expectation: "toHaveBeenCalled",
values: function(spyObj) {
return {
spyObj: spyObj,
times: callCount(spyObj),
calls: callsToString(spyObj)
};
}
});
referee.add("calledWithNew", {
assert: function(spy) {
verifyFakes.call(this, spy);
return spy.calledWithNew();
},
assertMessage:
"Expected ${spyObj} to be called with 'new' at least once but was never called with 'new'",
refuteMessage: "Expected ${spyObj} to not be called with 'new'",
expectation: "toHaveBeenCalledWithNew",
values: function(spyObj) {
return {
spyObj: spyObj
};
}
});
referee.add("alwaysCalledWithNew", {
assert: function(spy) {
verifyFakes.call(this, spy);
return spy.alwaysCalledWithNew();
},
assertMessage: "Expected ${spyObj} to always be called with 'new'",
refuteMessage: "Expected ${spyObj} to not always be called with 'new'",
expectation: "toAlwaysHaveBeenCalledWithNew",
values: function(spyObj) {
return {
spyObj: spyObj
};
}
});
referee.add("callOrder", {
assert: function(spy) {
var type = Object.prototype.toString.call(spy);
var isArray = type === "[object Array]";
var args = isArray ? spy : arguments;
verifyFakes.apply(this, args);
if (calledInOrder(args)) {
return true;
}
this.expected = [].join.call(args, ", ");
this.actual = orderByFirstCall(slice(args)).join(", ");
return false;
},
assertMessage:
"Expected ${expected} to be called in order but were called as ${actual}",
refuteMessage: "Expected ${expected} not to be called in order"
});
function addCallCountAssertion(count) {
referee.add("called" + count, {
assert: function(spy) {
verifyFakes.call(this, spy);
return spy["called" + count];
},
assertMessage:
"Expected ${spyObj} to be called ${expectedTimes} but was called ${times}${calls}",
refuteMessage:
"Expected ${spyObj} to not be called exactly ${expectedTimes}${calls}",
expectation: "toHaveBeenCalled" + count,
values: function(spyObj) {
return {
spyObj: spyObj,
expectedTimes: count.toLowerCase(),
times: callCount(spyObj),
calls: callsToString(spyObj)
};
}
});
}
addCallCountAssertion("Once");
addCallCountAssertion("Twice");
addCallCountAssertion("Thrice");
function valuesWithThis(spyObj, expectedThis) {
return {
spyObj: spyObj,
expectedThis: expectedThis,
actualThis: spyObj.printf ? spyObj.printf("%t") : ""
};
}
referee.add("calledOn", {
assert: function(spy, thisObj) {
verifyFakes.call(this, spy);
return spy.calledOn(thisObj);
},
assertMessage:
"Expected ${spyObj} to be called with ${expectedThis} as this but was called on ${actualThis}",
refuteMessage:
"Expected ${spyObj} not to be called with ${expectedThis} as this",
expectation: "toHaveBeenCalledOn",
values: valuesWithThis
});
referee.add("alwaysCalledOn", {
assert: function(spy, thisObj) {
verifyFakes.call(this, spy);
return spy.alwaysCalledOn(thisObj);
},
assertMessage:
"Expected ${spyObj} to always be called with ${expectedThis} as this but was called on ${actualThis}",
refuteMessage:
"Expected ${spyObj} not to always be called with ${expectedThis} as this",
expectation: "toHaveAlwaysBeenCalledOn",
values: valuesWithThis
});
function formattedArgs(args, i) {
var index = i;
var l, result;
for (l = args.length, result = []; index < l; ++index) {
push.call(result, format(args[index]));
}
return result.join(", ");
}
function spyAndCalls(spyObj) {
var expected = formattedArgs(arguments, 1);
var actual = spyObj.printf ? spyObj.printf("%C") : "";
return {
spyObj: spyObj,
actual: actual,
expected: expected
};
}
referee.add("calledWith", {
assert: function(spy) {
verifyFakes.call(this, spy);
return spy.calledWith.apply(spy, slice(arguments, 1));
},
assertMessage:
"Expected ${spyObj} to be called with arguments ${expected}${actual}",
refuteMessage:
"Expected ${spyObj} not to be called with arguments ${expected}${actual}",
expectation: "toHaveBeenCalledWith",
values: spyAndCalls
});
referee.add("alwaysCalledWith", {
assert: function(spy) {
verifyFakes.call(this, spy);
return spy.alwaysCalledWith.apply(spy, slice(arguments, 1));
},
assertMessage:
"Expected ${spyObj} to always be called with arguments ${expected}${actual}",
refuteMessage:
"Expected ${spyObj} not to always be called with arguments ${expected}${actual}",
expectation: "toHaveAlwaysBeenCalledWith",
values: spyAndCalls
});
referee.add("calledOnceWith", {
assert: function(spy) {
verifyFakes.call(this, spy);
return spy.calledOnce && spy.calledWith.apply(spy, slice(arguments, 1));
},
assertMessage:
"Expected ${spyObj} to be called once with arguments ${expected}${actual}",
refuteMessage:
"Expected ${spyObj} not to be called once with arguments ${expected}${actual}",
expectation: "toHaveBeenCalledOnceWith",
values: spyAndCalls
});
referee.add("calledWithExactly", {
assert: function(spy) {
verifyFakes.call(this, spy);
return spy.calledWithExactly.apply(spy, slice(arguments, 1));
},
assertMessage:
"Expected ${spyObj} to be called with exact arguments ${expected}${actual}",
refuteMessage:
"Expected ${spyObj} not to be called with exact arguments ${expected}${actual}",
expectation: "toHaveBeenCalledWithExactly",
values: spyAndCalls
});
referee.add("alwaysCalledWithExactly", {
assert: function(spy) {
verifyFakes.call(this, spy);
return spy.alwaysCalledWithExactly.apply(spy, slice(arguments, 1));
},
assertMessage:
"Expected ${spyObj} to always be called with exact arguments ${expected}${actual}",
refuteMessage:
"Expected ${spyObj} not to always be called with exact arguments ${expected}${actual}",
expectation: "toHaveAlwaysBeenCalledWithExactly",
values: spyAndCalls
});
referee.add("calledOnceWithExactly", {
assert: function(spy) {
verifyFakes.call(this, spy);
return spy.calledOnceWithExactly.apply(spy, slice(arguments, 1));
},
assertMessage:
"Expected ${spyObj} to be called once with exact arguments ${expected}${actual}",
refuteMessage:
"Expected ${spyObj} not to be once called with exact arguments ${expected}${actual}",
expectation: "toHaveBeenCalledOnceWithExactly",
values: spyAndCalls
});
referee.add("calledWithMatch", {
assert: function(spy) {
verifyFakes.call(this, spy);
return spy.calledWithMatch.apply(spy, slice(arguments, 1));
},
assertMessage:
"Expected ${spyObj} to be called with matching arguments ${expected}${actual}",
refuteMessage:
"Expected ${spyObj} not to be called with matching arguments ${expected}${actual}",
expectation: "toHaveBeenCalledWithMatch",
values: spyAndCalls
});
referee.add("alwaysCalledWithMatch", {
assert: function(spy) {
verifyFakes.call(this, spy);
return spy.alwaysCalledWithMatch.apply(spy, slice(arguments, 1));
},
assertMessage:
"Expected ${spyObj} to always be called with matching arguments ${expected}${actual}",
refuteMessage:
"Expected ${spyObj} not to always be called with matching arguments ${expected}${actual}",
expectation: "toHaveAlwaysBeenCalledWithMatch",
values: spyAndCalls
});
function spyAndException(spyObj, exception) {
return {
spyObj: spyObj,
actual: spyObj.printf ? spyObj.printf("%C") : "",
exception: exception // note: not actually used
};
}
referee.add("threw", {
assert: function(spy) {
verifyFakes.call(this, spy);
return spy.threw(arguments[1]);
},
assertMessage: "Expected ${spyObj} to throw an exception${actual}",
refuteMessage: "Expected ${spyObj} not to throw an exception${actual}",
expectation: "toHaveThrown",
values: spyAndException
});
referee.add("alwaysThrew", {
assert: function(spy) {
verifyFakes.call(this, spy);
return spy.alwaysThrew(arguments[1]);
},
assertMessage: "Expected ${spyObj} to always throw an exception${actual}",
refuteMessage:
"Expected ${spyObj} not to always throw an exception${actual}",
expectation: "toAlwaysHaveThrown",
values: spyAndException
});
referee.sinon = sinon;
module.exports = referee;