vitest-auto-spy 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/index.cjs +252 -221
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +68 -149
- package/dist/index.d.ts +68 -149
- package/dist/index.js +252 -221
- package/dist/index.js.map +1 -1
- package/package.json +18 -1
package/README.md
CHANGED
|
@@ -286,6 +286,10 @@ npm run test:coverage # 100% thresholds enforced
|
|
|
286
286
|
npm run build
|
|
287
287
|
```
|
|
288
288
|
|
|
289
|
+
Releases are automated: merging a PR into `master` bumps the version from the
|
|
290
|
+
Conventional Commit types and publishes to npm — see
|
|
291
|
+
[CONTRIBUTING.md → Releases](./CONTRIBUTING.md#releases).
|
|
292
|
+
|
|
289
293
|
If this package saved you time, a ⭐ on [GitHub](https://github.com/ASDAlexey/vitest-auto-spy)
|
|
290
294
|
helps others find it.
|
|
291
295
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,71 +1,116 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var vitest = require('vitest');
|
|
4
|
+
var javascriptStringify = require('javascript-stringify');
|
|
3
5
|
var rxjs = require('rxjs');
|
|
4
6
|
var operators = require('rxjs/operators');
|
|
5
7
|
var testing = require('@angular/core/testing');
|
|
6
|
-
var javascriptStringify = require('javascript-stringify');
|
|
7
|
-
var vitest = require('vitest');
|
|
8
8
|
|
|
9
|
-
// src/
|
|
9
|
+
// src/lib/accessor-spy.ts
|
|
10
|
+
function defineWithEmptyAccessors(obj, prop) {
|
|
11
|
+
Object.defineProperty(obj, prop, {
|
|
12
|
+
get() {
|
|
13
|
+
return void 0;
|
|
14
|
+
},
|
|
15
|
+
set(_value) {
|
|
16
|
+
},
|
|
17
|
+
configurable: true
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
function spyOnAccessor(autoSpy, accessorName, accessorType) {
|
|
21
|
+
const key = accessorName;
|
|
22
|
+
return accessorType === "setter" ? vitest.vi.spyOn(autoSpy, key, "set") : vitest.vi.spyOn(autoSpy, key, "get");
|
|
23
|
+
}
|
|
24
|
+
function createAccessorsSpies(autoSpy, gettersToSpyOn, settersToSpyOn) {
|
|
25
|
+
const accessorSpies = { getters: {}, setters: {} };
|
|
26
|
+
autoSpy["accessorSpies"] = accessorSpies;
|
|
27
|
+
gettersToSpyOn.forEach((getterName) => {
|
|
28
|
+
defineWithEmptyAccessors(autoSpy, getterName);
|
|
29
|
+
accessorSpies.getters[getterName] = spyOnAccessor(autoSpy, getterName, "getter");
|
|
30
|
+
});
|
|
31
|
+
settersToSpyOn.forEach((setterName) => {
|
|
32
|
+
if (!Object.prototype.hasOwnProperty.call(autoSpy, setterName)) {
|
|
33
|
+
defineWithEmptyAccessors(autoSpy, setterName);
|
|
34
|
+
}
|
|
35
|
+
accessorSpies.setters[setterName] = spyOnAccessor(autoSpy, setterName, "setter");
|
|
36
|
+
});
|
|
37
|
+
}
|
|
10
38
|
var ArgsMap = class {
|
|
11
|
-
|
|
12
|
-
this.map = {};
|
|
13
|
-
}
|
|
14
|
-
// `stringify` always serializes an argument array to a string, so the cast is safe.
|
|
39
|
+
#map = {};
|
|
15
40
|
set(key, value) {
|
|
16
|
-
this
|
|
41
|
+
this.#map[this.#serialize(key)] = value;
|
|
17
42
|
}
|
|
18
43
|
get(key) {
|
|
19
|
-
return this
|
|
44
|
+
return this.#map[this.#serialize(key)];
|
|
45
|
+
}
|
|
46
|
+
// Keys are always argument arrays, which `javascript-stringify` always renders
|
|
47
|
+
// to a string. `String(...)` keeps the result total against its `string |
|
|
48
|
+
// undefined` signature without an unreachable fallback branch.
|
|
49
|
+
#serialize(key) {
|
|
50
|
+
return String(javascriptStringify.stringify(key));
|
|
20
51
|
}
|
|
21
52
|
};
|
|
53
|
+
var MUST_BE_CALLED_WITH_PREAMBLE = (functionName) => `The function '${functionName}' was configured with 'mustBeCalledWith' and expects to be called with specific arguments. `;
|
|
54
|
+
var NO_ARGUMENTS_MESSAGE = `But the function was called without any arguments.`;
|
|
55
|
+
var actualArgumentsMessage = (actualArgs) => {
|
|
56
|
+
const formatted = String(javascriptStringify.stringify(actualArgs));
|
|
57
|
+
return `But the actual arguments were: ${formatted.substring(1, formatted.length - 1)}`;
|
|
58
|
+
};
|
|
22
59
|
var errorHandler = {
|
|
23
60
|
throwArgumentsError(actualArgs, functionName) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
errorMessage += `But the function was called without any arguments.`;
|
|
27
|
-
} else {
|
|
28
|
-
let formattedArgs = javascriptStringify.stringify(actualArgs);
|
|
29
|
-
formattedArgs = formattedArgs.substring(1, formattedArgs.length - 1);
|
|
30
|
-
errorMessage += `But the actual arguments were: ${formattedArgs}`;
|
|
31
|
-
}
|
|
32
|
-
throw new Error(errorMessage);
|
|
61
|
+
const detail = actualArgs.length === 0 ? NO_ARGUMENTS_MESSAGE : actualArgumentsMessage(actualArgs);
|
|
62
|
+
throw new Error(MUST_BE_CALLED_WITH_PREAMBLE(functionName) + detail);
|
|
33
63
|
}
|
|
34
64
|
};
|
|
65
|
+
|
|
66
|
+
// src/lib/constants.ts
|
|
67
|
+
var REPLAY_BUFFER_SIZE = 1;
|
|
68
|
+
|
|
69
|
+
// src/lib/spy-decoration.ts
|
|
70
|
+
function decorate(target, helpers) {
|
|
71
|
+
return Object.assign(target, helpers);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/lib/value-config-guards.ts
|
|
75
|
+
function isCompleteConfig(config) {
|
|
76
|
+
return "complete" in config;
|
|
77
|
+
}
|
|
78
|
+
function isNextValueConfig(config) {
|
|
79
|
+
return "value" in config;
|
|
80
|
+
}
|
|
81
|
+
function isErrorConfig(config) {
|
|
82
|
+
return "errorValue" in config;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// src/lib/observable-spy.ts
|
|
35
86
|
function createReplaySubject() {
|
|
36
|
-
return new rxjs.ReplaySubject(
|
|
87
|
+
return new rxjs.ReplaySubject(REPLAY_BUFFER_SIZE);
|
|
37
88
|
}
|
|
38
89
|
function mergeSubjectWithDefaultValues(subject, valuesConfigs) {
|
|
39
|
-
const onCompleteSubject = new rxjs.ReplaySubject(
|
|
90
|
+
const onCompleteSubject = new rxjs.ReplaySubject(REPLAY_BUFFER_SIZE);
|
|
40
91
|
const results$ = rxjs.from(valuesConfigs).pipe(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (!("complete" in valueConfig)) {
|
|
92
|
+
// Honor a delay on a completion entry before it stops the stream.
|
|
93
|
+
operators.concatMap(
|
|
94
|
+
(config) => isCompleteConfig(config) && config.complete && config.delay ? rxjs.of(config).pipe(operators.delay(config.delay)) : rxjs.of(config)
|
|
95
|
+
),
|
|
96
|
+
// Stop (and signal completion) as soon as a `{ complete: true }` entry arrives.
|
|
97
|
+
operators.takeWhile((config) => {
|
|
98
|
+
if (!isCompleteConfig(config)) {
|
|
49
99
|
return true;
|
|
50
100
|
}
|
|
51
|
-
if (
|
|
101
|
+
if (config.complete) {
|
|
52
102
|
onCompleteSubject.next();
|
|
53
103
|
return false;
|
|
54
104
|
}
|
|
55
105
|
return true;
|
|
56
106
|
}),
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
return rxjs.of(valueConfig.value);
|
|
107
|
+
// Map each remaining entry to its emission: a value, an error, or nothing.
|
|
108
|
+
operators.concatMap((config) => {
|
|
109
|
+
if (isNextValueConfig(config) && config.value) {
|
|
110
|
+
return config.delay ? rxjs.of(config.value).pipe(operators.delay(config.delay)) : rxjs.of(config.value);
|
|
63
111
|
}
|
|
64
|
-
if (
|
|
65
|
-
|
|
66
|
-
return rxjs.timer(valueConfig.delay).pipe(operators.switchMap(() => rxjs.throwError(() => valueConfig.errorValue)));
|
|
67
|
-
}
|
|
68
|
-
return rxjs.throwError(() => valueConfig.errorValue);
|
|
112
|
+
if (isErrorConfig(config) && config.errorValue) {
|
|
113
|
+
return config.delay ? rxjs.timer(config.delay).pipe(operators.switchMap(() => rxjs.throwError(() => config.errorValue))) : rxjs.throwError(() => config.errorValue);
|
|
69
114
|
}
|
|
70
115
|
return rxjs.EMPTY;
|
|
71
116
|
})
|
|
@@ -73,57 +118,65 @@ function mergeSubjectWithDefaultValues(subject, valuesConfigs) {
|
|
|
73
118
|
return rxjs.merge(results$, subject.pipe(operators.takeUntil(onCompleteSubject)));
|
|
74
119
|
}
|
|
75
120
|
function addObservableHelpers(objectToDecorate, providedSubject, onSubjectConfigured) {
|
|
76
|
-
objectToDecorate
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
121
|
+
decorate(objectToDecorate, {
|
|
122
|
+
nextWith: (value) => {
|
|
123
|
+
providedSubject.next(value);
|
|
124
|
+
onSubjectConfigured(providedSubject);
|
|
125
|
+
},
|
|
126
|
+
nextOneTimeWith: (value) => {
|
|
127
|
+
providedSubject.next(value);
|
|
128
|
+
providedSubject.complete();
|
|
129
|
+
onSubjectConfigured(providedSubject);
|
|
130
|
+
},
|
|
131
|
+
nextWithValues: (valuesConfigs) => {
|
|
132
|
+
if (valuesConfigs.length === 0) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
onSubjectConfigured(mergeSubjectWithDefaultValues(providedSubject, valuesConfigs));
|
|
136
|
+
},
|
|
137
|
+
throwWith: (value) => {
|
|
138
|
+
providedSubject.error(value);
|
|
139
|
+
onSubjectConfigured(providedSubject);
|
|
140
|
+
},
|
|
141
|
+
complete: () => {
|
|
142
|
+
providedSubject.complete();
|
|
143
|
+
onSubjectConfigured(providedSubject);
|
|
144
|
+
},
|
|
145
|
+
returnSubject: () => {
|
|
146
|
+
onSubjectConfigured(providedSubject);
|
|
147
|
+
return providedSubject;
|
|
88
148
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
onSubjectConfigured(providedSubject);
|
|
101
|
-
return providedSubject;
|
|
102
|
-
};
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function buildPerCallObservable(replaySubject, config) {
|
|
152
|
+
let observable = replaySubject.asObservable();
|
|
153
|
+
if (config.delay) {
|
|
154
|
+
observable = observable.pipe(operators.delay(config.delay));
|
|
155
|
+
}
|
|
156
|
+
if (!config.doNotComplete) {
|
|
157
|
+
observable = observable.pipe(operators.take(1));
|
|
158
|
+
}
|
|
159
|
+
return observable;
|
|
103
160
|
}
|
|
104
161
|
function addNextWithPerCall(objectToDecorate, returnValueContainer, onConfigured = () => void 0) {
|
|
105
|
-
objectToDecorate
|
|
106
|
-
|
|
107
|
-
|
|
162
|
+
decorate(objectToDecorate, {
|
|
163
|
+
nextWithPerCall: (valueConfigsPerCall) => {
|
|
164
|
+
const returnedSubjects = [];
|
|
165
|
+
if (valueConfigsPerCall.length === 0) {
|
|
166
|
+
return returnedSubjects;
|
|
167
|
+
}
|
|
168
|
+
const valuesPerCalls = [];
|
|
169
|
+
valueConfigsPerCall.forEach((config) => {
|
|
170
|
+
const replaySubject = createReplaySubject();
|
|
171
|
+
replaySubject.next(config.value);
|
|
172
|
+
returnedSubjects.push(replaySubject);
|
|
173
|
+
valuesPerCalls.push({ wrappedValue: buildPerCallObservable(replaySubject, config) });
|
|
174
|
+
});
|
|
175
|
+
returnValueContainer.valuesPerCalls = valuesPerCalls;
|
|
176
|
+
onConfigured(returnValueContainer);
|
|
108
177
|
return returnedSubjects;
|
|
109
178
|
}
|
|
110
|
-
|
|
111
|
-
valueConfigsPerCall.forEach((valueConfiguration) => {
|
|
112
|
-
const replaySubject = new rxjs.ReplaySubject(1);
|
|
113
|
-
replaySubject.next(valueConfiguration.value);
|
|
114
|
-
returnedSubjects.push(replaySubject);
|
|
115
|
-
let returnedObservable = replaySubject.asObservable();
|
|
116
|
-
if (valueConfiguration.delay) {
|
|
117
|
-
returnedObservable = returnedObservable.pipe(operators.delay(valueConfiguration.delay));
|
|
118
|
-
}
|
|
119
|
-
if (!valueConfiguration.doNotComplete) {
|
|
120
|
-
returnedObservable = returnedObservable.pipe(operators.take(1));
|
|
121
|
-
}
|
|
122
|
-
returnValueContainer.valuesPerCalls.push({ wrappedValue: returnedObservable });
|
|
123
|
-
});
|
|
124
|
-
onConfigured(returnValueContainer);
|
|
125
|
-
return returnedSubjects;
|
|
126
|
-
};
|
|
179
|
+
});
|
|
127
180
|
}
|
|
128
181
|
function addObservableHelpersToFunctionSpy(spyFunction, valueContainer) {
|
|
129
182
|
const subject = createReplaySubject();
|
|
@@ -146,108 +199,107 @@ function addObservableHelpersToCalledWithObject(calledWithObject, calledWithArgs
|
|
|
146
199
|
function createObservableWithValues(valuesConfigs, config) {
|
|
147
200
|
const subject = createReplaySubject();
|
|
148
201
|
const values$ = mergeSubjectWithDefaultValues(subject, valuesConfigs);
|
|
149
|
-
|
|
150
|
-
return { values$, subject };
|
|
151
|
-
}
|
|
152
|
-
return values$;
|
|
202
|
+
return config?.returnSubject ? { values$, subject } : values$;
|
|
153
203
|
}
|
|
154
204
|
function createObservablePropSpy() {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
205
|
+
const providedSubject = createReplaySubject();
|
|
206
|
+
let published$ = providedSubject;
|
|
207
|
+
const observableSpy = rxjs.defer(() => published$);
|
|
208
|
+
addObservableHelpers(observableSpy, providedSubject, (configuredSubject) => {
|
|
209
|
+
published$ = configuredSubject;
|
|
159
210
|
});
|
|
160
211
|
return observableSpy;
|
|
161
212
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
213
|
+
|
|
214
|
+
// src/lib/promise-spy.ts
|
|
215
|
+
function toResolvedPerCallValues(valueConfigsPerCall) {
|
|
216
|
+
return valueConfigsPerCall.map((config) => ({
|
|
217
|
+
wrappedValue: config.delay === void 0 ? Promise.resolve(config.value) : new Promise((resolve) => setTimeout(() => resolve(config.value), config.delay))
|
|
218
|
+
}));
|
|
219
|
+
}
|
|
220
|
+
function addPromiseHelpers(target, store) {
|
|
221
|
+
decorate(target, {
|
|
222
|
+
resolveWith: (value) => {
|
|
223
|
+
store({ value: Promise.resolve(value) });
|
|
224
|
+
},
|
|
225
|
+
rejectWith: (value) => {
|
|
226
|
+
store({ value, _isRejectedPromise: true });
|
|
227
|
+
},
|
|
228
|
+
resolveWithPerCall: (valueConfigsPerCall) => {
|
|
229
|
+
if (valueConfigsPerCall.length === 0) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
store({ value: void 0, valuesPerCalls: toResolvedPerCallValues(valueConfigsPerCall) });
|
|
173
233
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
};
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
function addPromiseHelpersToFunctionSpy(spyFunction, valueContainer) {
|
|
237
|
+
addPromiseHelpers(spyFunction, (container) => {
|
|
238
|
+
valueContainer.value = container.value;
|
|
239
|
+
valueContainer._isRejectedPromise = container._isRejectedPromise ?? false;
|
|
240
|
+
valueContainer.valuesPerCalls = container.valuesPerCalls ?? [];
|
|
241
|
+
});
|
|
182
242
|
}
|
|
183
243
|
function addPromiseHelpersToCalledWithObject(calledWithObject, calledWithArgs) {
|
|
184
|
-
calledWithObject
|
|
185
|
-
calledWithObject.argsToValuesMap.set(calledWithArgs,
|
|
186
|
-
};
|
|
187
|
-
calledWithObject.rejectWith = (value) => {
|
|
188
|
-
calledWithObject.argsToValuesMap.set(calledWithArgs, { value, _isRejectedPromise: true });
|
|
189
|
-
};
|
|
190
|
-
calledWithObject.resolveWithPerCall = (valueConfigsPerCall) => {
|
|
191
|
-
if (valueConfigsPerCall.length === 0) {
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
const valueContainer = { value: void 0, valuesPerCalls: [] };
|
|
195
|
-
valueConfigsPerCall.forEach((valueConfiguration) => {
|
|
196
|
-
valueContainer.valuesPerCalls.push({
|
|
197
|
-
wrappedValue: Promise.resolve(valueConfiguration.value),
|
|
198
|
-
delay: valueConfiguration.delay
|
|
199
|
-
});
|
|
200
|
-
});
|
|
201
|
-
calledWithObject.argsToValuesMap.set(calledWithArgs, valueContainer);
|
|
202
|
-
};
|
|
244
|
+
addPromiseHelpers(calledWithObject, (container) => {
|
|
245
|
+
calledWithObject.argsToValuesMap.set(calledWithArgs, container);
|
|
246
|
+
});
|
|
203
247
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
248
|
+
|
|
249
|
+
// src/lib/function-spy.ts
|
|
250
|
+
function isReturnValueContainer(value) {
|
|
251
|
+
return typeof value === "object" && value !== null && "value" in value;
|
|
252
|
+
}
|
|
253
|
+
function unwrapContainer(container) {
|
|
254
|
+
if (container._isRejectedPromise) {
|
|
255
|
+
return Promise.reject(container.value);
|
|
256
|
+
}
|
|
257
|
+
const wrapped = container.valuesPerCalls?.shift();
|
|
258
|
+
if (wrapped) {
|
|
259
|
+
return wrapped.wrappedValue;
|
|
260
|
+
}
|
|
261
|
+
return container.value;
|
|
262
|
+
}
|
|
263
|
+
function lookupConfigured(calledWithObject, actualArgs) {
|
|
264
|
+
const configured = calledWithObject.argsToValuesMap.get(actualArgs);
|
|
265
|
+
if (isReturnValueContainer(configured)) {
|
|
266
|
+
return { found: true, value: unwrapContainer(configured) };
|
|
211
267
|
}
|
|
212
|
-
return
|
|
268
|
+
return { found: false, value: void 0 };
|
|
213
269
|
}
|
|
214
270
|
function returnTheCorrectFakeValue(calledWithObject, mustBeCalledWithObject, valueContainer, actualArgs, functionName) {
|
|
215
271
|
if (calledWithObject.wasConfigured) {
|
|
216
|
-
const
|
|
217
|
-
if (
|
|
218
|
-
return
|
|
272
|
+
const match = lookupConfigured(calledWithObject, actualArgs);
|
|
273
|
+
if (match.found) {
|
|
274
|
+
return match.value;
|
|
219
275
|
}
|
|
220
276
|
}
|
|
221
277
|
if (mustBeCalledWithObject.wasConfigured) {
|
|
222
|
-
const
|
|
223
|
-
if (
|
|
224
|
-
return
|
|
278
|
+
const match = lookupConfigured(mustBeCalledWithObject, actualArgs);
|
|
279
|
+
if (match.found) {
|
|
280
|
+
return match.value;
|
|
225
281
|
}
|
|
226
282
|
errorHandler.throwArgumentsError(actualArgs, functionName);
|
|
227
283
|
}
|
|
228
284
|
return unwrapContainer(valueContainer);
|
|
229
285
|
}
|
|
230
|
-
function unwrapContainer(container) {
|
|
231
|
-
if (container._isRejectedPromise) {
|
|
232
|
-
return Promise.reject(container.value);
|
|
233
|
-
}
|
|
234
|
-
if (container.valuesPerCalls?.length) {
|
|
235
|
-
return getNextCallValue(container);
|
|
236
|
-
}
|
|
237
|
-
return container.value;
|
|
238
|
-
}
|
|
239
286
|
function addMethodsToCalledWith(calledWith, calledWithArgs) {
|
|
240
287
|
calledWith.wasConfigured = true;
|
|
241
|
-
calledWith
|
|
242
|
-
|
|
243
|
-
|
|
288
|
+
decorate(calledWith, {
|
|
289
|
+
mockReturnValue: (value) => {
|
|
290
|
+
calledWith.argsToValuesMap.set(calledWithArgs, { value });
|
|
291
|
+
}
|
|
292
|
+
});
|
|
244
293
|
addPromiseHelpersToCalledWithObject(calledWith, calledWithArgs);
|
|
245
294
|
addObservableHelpersToCalledWithObject(calledWith, calledWithArgs);
|
|
246
295
|
return calledWith;
|
|
247
296
|
}
|
|
297
|
+
function createCalledWithObject() {
|
|
298
|
+
return { wasConfigured: false, argsToValuesMap: new ArgsMap() };
|
|
299
|
+
}
|
|
248
300
|
function createFunctionSpy(name) {
|
|
249
|
-
const calledWithObject =
|
|
250
|
-
const mustBeCalledWithObject =
|
|
301
|
+
const calledWithObject = createCalledWithObject();
|
|
302
|
+
const mustBeCalledWithObject = createCalledWithObject();
|
|
251
303
|
const valueContainer = { value: void 0 };
|
|
252
304
|
const functionSpy = vitest.vi.fn(
|
|
253
305
|
(...actualArgs) => returnTheCorrectFakeValue(calledWithObject, mustBeCalledWithObject, valueContainer, actualArgs, name)
|
|
@@ -255,78 +307,56 @@ function createFunctionSpy(name) {
|
|
|
255
307
|
functionSpy.mockName(name);
|
|
256
308
|
addPromiseHelpersToFunctionSpy(functionSpy, valueContainer);
|
|
257
309
|
addObservableHelpersToFunctionSpy(functionSpy, valueContainer);
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}
|
|
262
|
-
function defineWithEmptyAccessors(obj, prop) {
|
|
263
|
-
Object.defineProperty(obj, prop, {
|
|
264
|
-
get() {
|
|
265
|
-
return void 0;
|
|
266
|
-
},
|
|
267
|
-
set(_value) {
|
|
268
|
-
},
|
|
269
|
-
configurable: true
|
|
310
|
+
const spy = decorate(functionSpy, {
|
|
311
|
+
calledWith: (...calledWithArgs) => addMethodsToCalledWith(calledWithObject, calledWithArgs),
|
|
312
|
+
mustBeCalledWith: (...calledWithArgs) => addMethodsToCalledWith(mustBeCalledWithObject, calledWithArgs)
|
|
270
313
|
});
|
|
314
|
+
return exposeAsSpy(spy);
|
|
271
315
|
}
|
|
272
|
-
function
|
|
273
|
-
|
|
274
|
-
return vitest.vi.spyOn(autoSpy, accessorName, "set");
|
|
275
|
-
}
|
|
276
|
-
return vitest.vi.spyOn(autoSpy, accessorName, "get");
|
|
277
|
-
}
|
|
278
|
-
function createAccessorsSpies(autoSpy, gettersToSpyOn, settersToSpyOn) {
|
|
279
|
-
autoSpy.accessorSpies = { getters: {}, setters: {} };
|
|
280
|
-
gettersToSpyOn.forEach((getterName) => {
|
|
281
|
-
defineWithEmptyAccessors(autoSpy, getterName);
|
|
282
|
-
autoSpy.accessorSpies.getters[getterName] = accessorSpyFactory(autoSpy, getterName, "getter");
|
|
283
|
-
});
|
|
284
|
-
settersToSpyOn.forEach((setterName) => {
|
|
285
|
-
if (!Object.prototype.hasOwnProperty.call(autoSpy, setterName)) {
|
|
286
|
-
defineWithEmptyAccessors(autoSpy, setterName);
|
|
287
|
-
}
|
|
288
|
-
autoSpy.accessorSpies.setters[setterName] = accessorSpyFactory(autoSpy, setterName, "setter");
|
|
289
|
-
});
|
|
316
|
+
function exposeAsSpy(spy) {
|
|
317
|
+
return spy;
|
|
290
318
|
}
|
|
319
|
+
|
|
320
|
+
// src/lib/create-spy-from-class.ts
|
|
321
|
+
var EMPTY_CONFIGURATION = {
|
|
322
|
+
methodsToSpyOn: [],
|
|
323
|
+
observablePropsToSpyOn: [],
|
|
324
|
+
settersToSpyOn: [],
|
|
325
|
+
gettersToSpyOn: []
|
|
326
|
+
};
|
|
291
327
|
function extractMethodsFromObject(obj) {
|
|
292
328
|
const descriptors = Object.getOwnPropertyDescriptors(obj);
|
|
293
|
-
return Object.keys(descriptors).
|
|
294
|
-
if (name !== "constructor" && !descriptors[name].get) {
|
|
295
|
-
names.push(name);
|
|
296
|
-
}
|
|
297
|
-
return names;
|
|
298
|
-
}, []);
|
|
329
|
+
return Object.keys(descriptors).filter((name) => name !== "constructor" && !descriptors[name]?.get);
|
|
299
330
|
}
|
|
300
|
-
function getAllMethodNames(
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
331
|
+
function getAllMethodNames(prototype) {
|
|
332
|
+
const methods = /* @__PURE__ */ new Set();
|
|
333
|
+
let current = prototype;
|
|
334
|
+
while (current) {
|
|
335
|
+
const parentObj = Object.getPrototypeOf(current);
|
|
304
336
|
if (parentObj) {
|
|
305
|
-
|
|
337
|
+
extractMethodsFromObject(current).forEach((name) => methods.add(name));
|
|
306
338
|
}
|
|
307
|
-
|
|
339
|
+
current = parentObj;
|
|
308
340
|
}
|
|
309
|
-
return methods;
|
|
341
|
+
return [...methods];
|
|
310
342
|
}
|
|
311
|
-
function
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
let observablePropsToSpyOn = [];
|
|
315
|
-
let settersToSpyOn = [];
|
|
316
|
-
let gettersToSpyOn = [];
|
|
317
|
-
if (methodsToSpyOnOrConfig) {
|
|
318
|
-
if (Array.isArray(methodsToSpyOnOrConfig)) {
|
|
319
|
-
methodsToSpyOn = methodsToSpyOnOrConfig;
|
|
320
|
-
} else {
|
|
321
|
-
methodsToSpyOn = methodsToSpyOnOrConfig.methodsToSpyOn || [];
|
|
322
|
-
observablePropsToSpyOn = methodsToSpyOnOrConfig.observablePropsToSpyOn || [];
|
|
323
|
-
settersToSpyOn = methodsToSpyOnOrConfig.settersToSpyOn || [];
|
|
324
|
-
gettersToSpyOn = methodsToSpyOnOrConfig.gettersToSpyOn || [];
|
|
325
|
-
}
|
|
343
|
+
function resolveConfiguration(methodsToSpyOnOrConfig) {
|
|
344
|
+
if (!methodsToSpyOnOrConfig) {
|
|
345
|
+
return { ...EMPTY_CONFIGURATION };
|
|
326
346
|
}
|
|
327
|
-
if (
|
|
328
|
-
|
|
347
|
+
if (Array.isArray(methodsToSpyOnOrConfig)) {
|
|
348
|
+
return { ...EMPTY_CONFIGURATION, methodsToSpyOn: methodsToSpyOnOrConfig };
|
|
329
349
|
}
|
|
350
|
+
return {
|
|
351
|
+
methodsToSpyOn: methodsToSpyOnOrConfig.methodsToSpyOn ?? [],
|
|
352
|
+
observablePropsToSpyOn: methodsToSpyOnOrConfig.observablePropsToSpyOn ?? [],
|
|
353
|
+
settersToSpyOn: methodsToSpyOnOrConfig.settersToSpyOn ?? [],
|
|
354
|
+
gettersToSpyOn: methodsToSpyOnOrConfig.gettersToSpyOn ?? []
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
function createSpyFromClass(ObjectClass, methodsToSpyOnOrConfig) {
|
|
358
|
+
const { methodsToSpyOn, observablePropsToSpyOn, settersToSpyOn, gettersToSpyOn } = resolveConfiguration(methodsToSpyOnOrConfig);
|
|
359
|
+
const methodNames = methodsToSpyOn.length > 0 ? methodsToSpyOn : getAllMethodNames(ObjectClass.prototype);
|
|
330
360
|
const autoSpy = {};
|
|
331
361
|
observablePropsToSpyOn.forEach((observablePropName) => {
|
|
332
362
|
autoSpy[observablePropName] = createObservablePropSpy();
|
|
@@ -344,7 +374,8 @@ function provideAutoSpy(ObjectClass, methodsToSpyOnOrConfig) {
|
|
|
344
374
|
};
|
|
345
375
|
}
|
|
346
376
|
function injectSpy(token) {
|
|
347
|
-
|
|
377
|
+
const injected = testing.TestBed.inject(token);
|
|
378
|
+
return injected;
|
|
348
379
|
}
|
|
349
380
|
function mockReadonlyProp(object, property, value) {
|
|
350
381
|
Object.defineProperty(object, property, { get: () => value, configurable: true });
|