1 | import _ from './wrap/lodash'
|
2 | import argsMatch from './args-match'
|
3 | import callsStore from './store/calls'
|
4 | import log from './log'
|
5 | import store from './store'
|
6 | import stringifyArgs from './stringify/arguments'
|
7 | import stubbingsStore from './store/stubbings'
|
8 | import notifyAfterSatisfaction from './matchers/notify-after-satisfaction'
|
9 |
|
10 | export 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 |
|
21 | var ensureRehearsalOccurred = (last) => {
|
22 | if (!last) {
|
23 | log.error('td.verify', `\
|
24 | No test double invocation detected for \`verify()\`.
|
25 |
|
26 | Usage:
|
27 | verify(myTestDouble('foo'))\
|
28 | `)
|
29 | }
|
30 | }
|
31 |
|
32 | const notifyMatchers = (testDouble, expectedArgs, config) => {
|
33 | _.each(callsStore.where(testDouble, expectedArgs, config), (invocation) => {
|
34 | notifyAfterSatisfaction(expectedArgs, invocation.args)
|
35 | })
|
36 | }
|
37 |
|
38 | var 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 |
|
49 | var unsatisfiedErrorMessage = (testDouble, args, config) =>
|
50 | baseSummary(testDouble, args, config) +
|
51 | matchedInvocationSummary(testDouble, args, config) +
|
52 | invocationSummary(testDouble, args, config)
|
53 |
|
54 | var stringifyName = (testDouble) => {
|
55 | const name = store.for(testDouble).name
|
56 | return name ? ` \`${name}\`` : ''
|
57 | }
|
58 |
|
59 | var baseSummary = (testDouble, args, config) =>
|
60 | `\
|
61 | Unsatisfied verification on test double${stringifyName(testDouble)}.
|
62 |
|
63 | Wanted:
|
64 | - called with \`(${stringifyArgs(args)})\`${timesMessage(config)}${ignoreMessage(config)}.\
|
65 | `
|
66 |
|
67 | var 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 |
|
78 | var 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 |
|
91 | var pluralize = (x, msg) =>
|
92 | `${x} ${msg}${x === 1 ? '' : 's'}`
|
93 |
|
94 | var timesMessage = (config) =>
|
95 | config.times != null
|
96 | ? ` ${pluralize(config.times, 'time')}`
|
97 | : ''
|
98 |
|
99 | var ignoreMessage = (config) =>
|
100 | config.ignoreExtraArgs != null
|
101 | ? ', ignoring any additional arguments'
|
102 | : ''
|