const indexMap = [ 'firstCall', 'secondCall', 'thirdCall', ] export function spy() { return stub() } export function stub(returnFunc = null) { if (returnFunc && typeof(returnFunc) !== 'function') { throw new Error('stub() was called with non-function argument') } let manualReturners = new Map() let nextManual = null let returner = returnFunc ? returnFunc : null let calls = [] let func = function(...args) { func.called = true calls.push(args) func.lastCall = args if (func.callCount < indexMap.length) { func[indexMap[func.callCount]] = args } func.callCount++ if (manualReturners.has(func.callCount -1)) { return manualReturners.get(func.callCount -1)(...args) } if (returner) { return returner(...args) } } func.lastCall = null func.called = false func.callCount = 0 func.findCall = function(fn) { for (let call of calls) { if (fn(call)) return call } return null } func.getCall = function(i) { return calls[i] } func.getCallN = function(i) { return calls[i - 1] } func.onCall = function(i) { if (i !== null && typeof(i) !== 'number') { throw new Error('onCall must be called with either null or number') } nextManual = i return func } func.onCallN = function(i) { return func.onCall(i - 1) } func.reset = function() { func.lastCall = null func.called = false func.callCount = 0 manualReturners.clear() for (let i = 0; i < indexMap.length; i++) { func[indexMap[i]] = null } returner = returnFunc ? returnFunc : null calls.splice(0, calls.length) return func } func.returns = function(data) { func.returnWith(function() { return data }) return func } func.throws = function(data) { func.returnWith(function() { throw data }) return func } func.resolves = function(data) { func.returnWith(function() { return Promise.resolve(data) }) return func } func.rejects = function(data) { func.returnWith(function() { return Promise.reject(data) }) return func } func.returnWith = function(returnFunc) { if (typeof(returnFunc) !== 'function') { throw new Error('stub() was called with non-function argument') } if (nextManual !== null) { manualReturners.set(nextManual, returnFunc) nextManual = null } else { returner = returnFunc } return func } for (let i = 0; i < indexMap.length; i++) { func[indexMap[i]] = null } return func }