UNPKG

3.49 kBJavaScriptView Raw
1import _ from './wrap/lodash'
2import argsMatch from './args-match'
3import callsStore from './store/calls'
4import log from './log'
5import store from './store'
6import stringifyArgs from './stringify/arguments'
7import stubbingsStore from './store/stubbings'
8import notifyAfterSatisfaction from './matchers/notify-after-satisfaction'
9
10export default (__userDoesRehearsalInvocationHere__, config = {}) => {
11 const last = callsStore.pop()
12 ensureRehearsalOccurred(last)
13 if (callsStore.wasInvoked(last.testDouble, last.args, config)) {
14 notifyMatchers(last.testDouble, last.args, config)
15 warnIfStubbed(last.testDouble, last.args)
16 } else {
17 log.fail(unsatisfiedErrorMessage(last.testDouble, last.args, config))
18 }
19}
20
21var ensureRehearsalOccurred = (last) => {
22 if (!last) {
23 log.error('td.verify', `\
24No test double invocation detected for \`verify()\`.
25
26 Usage:
27 verify(myTestDouble('foo'))\
28`)
29 }
30}
31
32const notifyMatchers = (testDouble, expectedArgs, config) => {
33 _.each(callsStore.where(testDouble, expectedArgs, config), (invocation) => {
34 notifyAfterSatisfaction(expectedArgs, invocation.args)
35 })
36}
37
38var warnIfStubbed = (testDouble, actualArgs) => {
39 if (_.some(stubbingsStore.for(testDouble), (stubbing) =>
40 argsMatch(stubbing.args, actualArgs, stubbing.config))
41 ) {
42 log.warn('td.verify',
43 `test double${stringifyName(testDouble)} was both stubbed and verified with arguments (${stringifyArgs(actualArgs)}), which is redundant and probably unnecessary.`,
44 'https://github.com/testdouble/testdouble.js/blob/master/docs/B-frequently-asked-questions.md#why-shouldnt-i-call-both-tdwhen-and-tdverify-for-a-single-interaction-with-a-test-double'
45 )
46 }
47}
48
49var unsatisfiedErrorMessage = (testDouble, args, config) =>
50 baseSummary(testDouble, args, config) +
51 matchedInvocationSummary(testDouble, args, config) +
52 invocationSummary(testDouble, args, config)
53
54var stringifyName = (testDouble) => {
55 const name = store.for(testDouble).name
56 return name ? ` \`${name}\`` : ''
57}
58
59var baseSummary = (testDouble, args, config) =>
60 `\
61Unsatisfied verification on test double${stringifyName(testDouble)}.
62
63 Wanted:
64 - called with \`(${stringifyArgs(args)})\`${timesMessage(config)}${ignoreMessage(config)}.\
65`
66
67var invocationSummary = (testDouble, args, config) => {
68 const calls = callsStore.for(testDouble)
69 if (calls.length === 0) {
70 return '\n\n But there were no invocations of the test double.'
71 } else {
72 return _.reduce(calls, (desc, call) =>
73 desc + `\n - called with \`(${stringifyArgs(call.args)})\`.`
74 , '\n\n All calls of the test double, in order were:')
75 }
76}
77
78var matchedInvocationSummary = (testDouble, args, config) => {
79 const calls = callsStore.where(testDouble, args, config)
80 const expectedCalls = config.times || 0
81
82 if (calls.length === 0 || calls.length > expectedCalls) {
83 return ''
84 } else {
85 return _.reduce(_.groupBy(calls, 'args'), (desc, callsMatchingArgs, args) =>
86 desc + `\n - called ${pluralize(callsMatchingArgs.length, 'time')} with \`(${stringifyArgs(callsMatchingArgs[0].args)})\`.`
87 , `\n\n ${pluralize(calls.length, 'call')} that satisfied this verification:`)
88 }
89}
90
91var pluralize = (x, msg) =>
92 `${x} ${msg}${x === 1 ? '' : 's'}`
93
94var timesMessage = (config) =>
95 config.times != null
96 ? ` ${pluralize(config.times, 'time')}`
97 : ''
98
99var ignoreMessage = (config) =>
100 config.ignoreExtraArgs != null
101 ? ', ignoring any additional arguments'
102 : ''