/
calltracker.js
96 lines (84 loc) Β· 2.29 KB
/
calltracker.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
'use strict';
const {
ArrayPrototypePush,
Error,
FunctionPrototype,
ReflectApply,
SafeSet,
} = primordials;
const {
codes: {
ERR_UNAVAILABLE_DURING_EXIT,
},
} = require('internal/errors');
const AssertionError = require('internal/assert/assertion_error');
const {
validateUint32,
} = require('internal/validators');
const noop = FunctionPrototype;
class CallTracker {
#callChecks = new SafeSet()
calls(fn, exact = 1) {
if (process._exiting)
throw new ERR_UNAVAILABLE_DURING_EXIT();
if (typeof fn === 'number') {
exact = fn;
fn = noop;
} else if (fn === undefined) {
fn = noop;
}
validateUint32(exact, 'exact', true);
const context = {
exact,
actual: 0,
// eslint-disable-next-line no-restricted-syntax
stackTrace: new Error(),
name: fn.name || 'calls'
};
const callChecks = this.#callChecks;
callChecks.add(context);
return function() {
context.actual++;
if (context.actual === context.exact) {
// Once function has reached its call count remove it from
// callChecks set to prevent memory leaks.
callChecks.delete(context);
}
// If function has been called more than expected times, add back into
// callchecks.
if (context.actual === context.exact + 1) {
callChecks.add(context);
}
return ReflectApply(fn, this, arguments);
};
}
report() {
const errors = [];
for (const context of this.#callChecks) {
// If functions have not been called exact times
if (context.actual !== context.exact) {
const message = `Expected the ${context.name} function to be ` +
`executed ${context.exact} time(s) but was ` +
`executed ${context.actual} time(s).`;
ArrayPrototypePush(errors, {
message,
actual: context.actual,
expected: context.exact,
operator: context.name,
stack: context.stackTrace
});
}
}
return errors;
}
verify() {
const errors = this.report();
if (errors.length > 0) {
throw new AssertionError({
message: 'Function(s) were not called the expected number of times',
details: errors,
});
}
}
}
module.exports = CallTracker;