1const mustCallChecks = []; 2 3function runCallChecks(exitCode) { 4 if (exitCode !== 0) return; 5 6 const failed = mustCallChecks.filter(function(context) { 7 return context.actual !== context.expected; 8 }); 9 10 failed.forEach(function(context) { 11 console.log('Mismatched %s function calls. Expected %d, actual %d.', 12 context.name, 13 context.expected, 14 context.actual); 15 console.log(context.stack.split('\n').slice(2).join('\n')); 16 }); 17 18 if (failed.length) process.exit(1); 19} 20 21exports.mustCall = function(fn, expected) { 22 if (typeof fn === 'number') { 23 expected = fn; 24 fn = noop; 25 } else if (fn === undefined) { 26 fn = noop; 27 } 28 29 if (expected === undefined) 30 expected = 1; 31 else if (typeof expected !== 'number') 32 throw new TypeError(`Invalid expected value: ${expected}`); 33 34 const context = { 35 expected: expected, 36 actual: 0, 37 stack: (new Error()).stack, 38 name: fn.name || '<anonymous>' 39 }; 40 41 // add the exit listener only once to avoid listener leak warnings 42 if (mustCallChecks.length === 0) process.on('exit', runCallChecks); 43 44 mustCallChecks.push(context); 45 46 return function() { 47 context.actual++; 48 return fn.apply(this, arguments); 49 }; 50}; 51 52// Crash the process on unhandled rejections. 53exports.crashOnUnhandledRejection = function() { 54 process.on('unhandledRejection', 55 (err) => process.nextTick(() => { throw err; })); 56}; 57