UNPKG

testdouble

Version:

A minimal test double library for TDD with JavaScript

117 lines (102 loc) 3.44 kB
import _ from '../wrap/lodash' import argsMatch from '../args-match' import isCallback from '../matchers/is-callback' import notifyAfterSatisfaction from '../matchers/notify-after-satisfaction' import config from '../config' import log from '../log' import store from './index' export default { add (testDouble, args, stubbedValues, config) { return store.for(testDouble).stubbings.push({ callCount: 0, stubbedValues, args: config.cloneArgs ? _.cloneDeep(args) : args, config }) }, invoke (testDouble, actualArgs, actualContext) { const stubbing = stubbingFor(testDouble, actualArgs) if (stubbing) { notifyAfterSatisfaction(stubbing.args, actualArgs) return executePlan(stubbing, actualArgs, actualContext) } }, for (testDouble) { return store.for(testDouble).stubbings } } const stubbingFor = (testDouble, actualArgs) => _.findLast(store.for(testDouble).stubbings, stubbing => isSatisfied(stubbing, actualArgs)) const executePlan = (stubbing, actualArgs, actualContext) => { const value = stubbedValueFor(stubbing) stubbing.callCount += 1 invokeCallbackFor(stubbing, actualArgs) switch (stubbing.config.plan) { case 'thenReturn': return value case 'thenDo': return value.apply(actualContext, actualArgs) case 'thenThrow': throw value case 'thenResolve': return createPromise(stubbing, value, true) case 'thenReject': return createPromise(stubbing, value, false) } } const invokeCallbackFor = (stubbing, actualArgs) => { if (_.some(stubbing.args, isCallback)) { _.each(stubbing.args, (expectedArg, i) => { if (isCallback(expectedArg)) { callCallback(stubbing, actualArgs[i], callbackArgs(stubbing, expectedArg)) } }) } } const callbackArgs = (stubbing, expectedArg) => { if (expectedArg.args != null) { return expectedArg.args } else if (stubbing.config.plan === 'thenCallback') { return stubbing.stubbedValues } else { return [] } } const callCallback = (stubbing, callback, args) => { if (stubbing.config.delay) { _.delay(callback, stubbing.config.delay, ...args) } else if (stubbing.config.defer) { _.defer(callback, ...args) } else { callback(...args) // eslint-disable-line } } const createPromise = (stubbing, value, willResolve) => { const Promise = config().promiseConstructor ensurePromise(Promise) return new Promise((resolve, reject) => { callCallback(stubbing, () => willResolve ? resolve(value) : reject(value) , [value]) }) } const stubbedValueFor = (stubbing) => stubbing.callCount < stubbing.stubbedValues.length ? stubbing.stubbedValues[stubbing.callCount] : _.last(stubbing.stubbedValues) const isSatisfied = (stubbing, actualArgs) => argsMatch(stubbing.args, actualArgs, stubbing.config) && hasTimesRemaining(stubbing) const hasTimesRemaining = (stubbing) => stubbing.config.times == null ? true : stubbing.callCount < stubbing.config.times const ensurePromise = (Promise) => { if (Promise == null) { return log.error('td.when', `\ no promise constructor is set (perhaps this runtime lacks a native Promise function?), which means this stubbing can't return a promise to your subject under test, resulting in this error. To resolve the issue, set a promise constructor with \`td.config\`, like this: td.config({ promiseConstructor: require('bluebird') })\ `) } }