strong-mock 9.0.0-beta.1 → 9.0.0-beta.3
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 +164 -139
- package/dist/index.js +26 -222
- package/dist/index.js.map +1 -1
- package/dist/matchers/{is-partial.d.ts → contains-object.d.ts} +6 -5
- package/dist/matchers/deep-equals.d.ts +1 -1
- package/dist/matchers/is-plain-object.d.ts +1 -1
- package/dist/matchers/it.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -16,7 +16,6 @@ const MATCHER_SYMBOL = Symbol('matcher');
|
|
|
16
16
|
/**
|
|
17
17
|
* Used to test if an expectation is an argument is a custom matcher.
|
|
18
18
|
*/
|
|
19
|
-
|
|
20
19
|
function isMatcher(f) {
|
|
21
20
|
return !!(f && f[MATCHER_SYMBOL]);
|
|
22
21
|
}
|
|
@@ -51,10 +50,8 @@ const getMatcherDiffs = (matchers, args) => {
|
|
|
51
50
|
* fn(2) === 42
|
|
52
51
|
* fn(-1) // throws
|
|
53
52
|
*/
|
|
54
|
-
|
|
55
53
|
const matches = (predicate, options) => {
|
|
56
54
|
var _options$toString, _options$getDiff;
|
|
57
|
-
|
|
58
55
|
// We can't use destructuring with default values because `options` is optional,
|
|
59
56
|
// so it needs a default value of `{}`, which will come with a native `toString`.
|
|
60
57
|
const toString = (_options$toString = options == null ? void 0 : options.toString) != null ? _options$toString : () => `Matcher(${predicate.toString()})`;
|
|
@@ -73,7 +70,6 @@ const matches = (predicate, options) => {
|
|
|
73
70
|
expected: actual
|
|
74
71
|
};
|
|
75
72
|
}
|
|
76
|
-
|
|
77
73
|
return getDiff(actual);
|
|
78
74
|
}
|
|
79
75
|
};
|
|
@@ -84,11 +80,9 @@ const printProperty = property => {
|
|
|
84
80
|
if (property === ApplyProp) {
|
|
85
81
|
return '';
|
|
86
82
|
}
|
|
87
|
-
|
|
88
83
|
if (typeof property === 'symbol') {
|
|
89
84
|
return `[${property.toString()}]`;
|
|
90
85
|
}
|
|
91
|
-
|
|
92
86
|
return `.${property}`;
|
|
93
87
|
};
|
|
94
88
|
const printValue = arg => {
|
|
@@ -96,20 +90,15 @@ const printValue = arg => {
|
|
|
96
90
|
if (isMatcher(arg)) {
|
|
97
91
|
return arg.toString();
|
|
98
92
|
}
|
|
99
|
-
|
|
100
93
|
return jestMatcherUtils.stringify(arg);
|
|
101
94
|
};
|
|
102
|
-
|
|
103
95
|
const printArgs = args => args.map(arg => printValue(arg)).join(', ');
|
|
104
|
-
|
|
105
96
|
const printCall = (property, args) => {
|
|
106
97
|
const prettyProperty = printProperty(property);
|
|
107
|
-
|
|
108
98
|
if (args) {
|
|
109
99
|
const prettyArgs = printArgs(args);
|
|
110
100
|
return `mock${jestMatcherUtils.RECEIVED_COLOR(`${prettyProperty}(${prettyArgs})`)}`;
|
|
111
101
|
}
|
|
112
|
-
|
|
113
102
|
return `mock${jestMatcherUtils.RECEIVED_COLOR(`${prettyProperty}`)}`;
|
|
114
103
|
};
|
|
115
104
|
const printReturns = ({
|
|
@@ -118,7 +107,6 @@ const printReturns = ({
|
|
|
118
107
|
value
|
|
119
108
|
}, min, max) => {
|
|
120
109
|
let thenPrefix = '';
|
|
121
|
-
|
|
122
110
|
if (isPromise) {
|
|
123
111
|
if (isError) {
|
|
124
112
|
thenPrefix += 'thenReject';
|
|
@@ -130,16 +118,13 @@ const printReturns = ({
|
|
|
130
118
|
} else {
|
|
131
119
|
thenPrefix += 'thenReturn';
|
|
132
120
|
}
|
|
133
|
-
|
|
134
121
|
return `.${thenPrefix}(${jestMatcherUtils.RECEIVED_COLOR(printValue(value))}).between(${min}, ${max})`;
|
|
135
122
|
};
|
|
136
123
|
const printWhen = (property, args) => {
|
|
137
124
|
const prettyProperty = printProperty(property);
|
|
138
|
-
|
|
139
125
|
if (args) {
|
|
140
126
|
return `when(() => mock${jestMatcherUtils.EXPECTED_COLOR(`${prettyProperty}(${printArgs(args)})`)})`;
|
|
141
127
|
}
|
|
142
|
-
|
|
143
128
|
return `when(() => mock${jestMatcherUtils.EXPECTED_COLOR(`${printProperty(property)}`)})`;
|
|
144
129
|
};
|
|
145
130
|
const printExpectation = (property, args, returnValue, min, max) => `${printWhen(property, args)}${printReturns(returnValue, min, max)}`;
|
|
@@ -155,7 +140,6 @@ set an expectation for it.
|
|
|
155
140
|
|
|
156
141
|
${printRemainingExpectations(expectations)}`));
|
|
157
142
|
}
|
|
158
|
-
|
|
159
143
|
}
|
|
160
144
|
|
|
161
145
|
const printArgsDiff = (expected, actual) => {
|
|
@@ -163,14 +147,12 @@ const printArgsDiff = (expected, actual) => {
|
|
|
163
147
|
omitAnnotationLines: true
|
|
164
148
|
});
|
|
165
149
|
/* istanbul ignore next this is not expected in practice */
|
|
166
|
-
|
|
167
150
|
if (!diff) {
|
|
168
151
|
return '';
|
|
169
152
|
}
|
|
170
|
-
|
|
171
153
|
const ansilessDiffLines = stripAnsi__default["default"](diff).split('\n');
|
|
172
|
-
let relevantDiffLines;
|
|
173
|
-
|
|
154
|
+
let relevantDiffLines;
|
|
155
|
+
// Strip Array [ ... ] surroundings.
|
|
174
156
|
if (!expected.length) {
|
|
175
157
|
// - Array []
|
|
176
158
|
// + Array [
|
|
@@ -188,20 +170,16 @@ const printArgsDiff = (expected, actual) => {
|
|
|
188
170
|
// ...
|
|
189
171
|
// ]
|
|
190
172
|
relevantDiffLines = ansilessDiffLines.slice(1, -1);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
|
|
173
|
+
}
|
|
174
|
+
// Strip the trailing comma.
|
|
194
175
|
const lastLine = relevantDiffLines[relevantDiffLines.length - 1].slice(0, -1);
|
|
195
176
|
const coloredDiffLines = [...relevantDiffLines.slice(0, -1), lastLine].map(line => {
|
|
196
177
|
const first = line.charAt(0);
|
|
197
|
-
|
|
198
178
|
switch (first) {
|
|
199
179
|
case '-':
|
|
200
180
|
return jestMatcherUtils.EXPECTED_COLOR(line);
|
|
201
|
-
|
|
202
181
|
case '+':
|
|
203
182
|
return jestMatcherUtils.RECEIVED_COLOR(line);
|
|
204
|
-
|
|
205
183
|
default:
|
|
206
184
|
return line;
|
|
207
185
|
}
|
|
@@ -210,11 +188,9 @@ const printArgsDiff = (expected, actual) => {
|
|
|
210
188
|
};
|
|
211
189
|
const printExpectationDiff = (e, args) => {
|
|
212
190
|
var _e$args;
|
|
213
|
-
|
|
214
191
|
if (!((_e$args = e.args) != null && _e$args.length)) {
|
|
215
192
|
return '';
|
|
216
193
|
}
|
|
217
|
-
|
|
218
194
|
const {
|
|
219
195
|
actual,
|
|
220
196
|
expected
|
|
@@ -223,7 +199,6 @@ const printExpectationDiff = (e, args) => {
|
|
|
223
199
|
};
|
|
224
200
|
const printDiffForAllExpectations = (expectations, actual) => expectations.map(e => {
|
|
225
201
|
const diff = printExpectationDiff(e, actual);
|
|
226
|
-
|
|
227
202
|
if (diff) {
|
|
228
203
|
return `${e.toString()}
|
|
229
204
|
${jestMatcherUtils.EXPECTED_COLOR('- Expected')}
|
|
@@ -231,7 +206,6 @@ ${jestMatcherUtils.RECEIVED_COLOR('+ Received')}
|
|
|
231
206
|
|
|
232
207
|
${diff}`;
|
|
233
208
|
}
|
|
234
|
-
|
|
235
209
|
return undefined;
|
|
236
210
|
}).filter(x => x).join('\n\n');
|
|
237
211
|
|
|
@@ -239,18 +213,15 @@ class UnexpectedCall extends Error {
|
|
|
239
213
|
constructor(property, args, expectations) {
|
|
240
214
|
const header = `Didn't expect ${printCall(property, args)} to be called.`;
|
|
241
215
|
const propertyExpectations = expectations.filter(e => e.property === property);
|
|
242
|
-
|
|
243
216
|
if (propertyExpectations.length) {
|
|
244
217
|
var _propertyExpectations;
|
|
245
|
-
|
|
246
218
|
super(jestMatcherUtils.DIM_COLOR(`${header}
|
|
247
219
|
|
|
248
220
|
Remaining expectations:
|
|
249
|
-
${printDiffForAllExpectations(propertyExpectations, args)}`));
|
|
221
|
+
${printDiffForAllExpectations(propertyExpectations, args)}`));
|
|
222
|
+
// If we have a single expectation we can attach the actual/expected args
|
|
250
223
|
// to the error instance, so that an IDE may show its own diff for them.
|
|
251
|
-
|
|
252
224
|
this.matcherResult = void 0;
|
|
253
|
-
|
|
254
225
|
if (propertyExpectations.length === 1 && (_propertyExpectations = propertyExpectations[0].args) != null && _propertyExpectations.length) {
|
|
255
226
|
const {
|
|
256
227
|
actual,
|
|
@@ -268,11 +239,9 @@ No remaining expectations.`));
|
|
|
268
239
|
this.matcherResult = void 0;
|
|
269
240
|
}
|
|
270
241
|
}
|
|
271
|
-
|
|
272
242
|
}
|
|
273
243
|
|
|
274
244
|
exports.UnexpectedProperty = void 0;
|
|
275
|
-
|
|
276
245
|
(function (UnexpectedProperty) {
|
|
277
246
|
/**
|
|
278
247
|
* Throw an error immediately.
|
|
@@ -307,7 +276,6 @@ exports.UnexpectedProperty = void 0;
|
|
|
307
276
|
* // Will throw "Didn't expect foo(42) to be called".
|
|
308
277
|
* foo(42);
|
|
309
278
|
*/
|
|
310
|
-
|
|
311
279
|
UnexpectedProperty[UnexpectedProperty["CALL_THROW"] = 1] = "CALL_THROW";
|
|
312
280
|
})(exports.UnexpectedProperty || (exports.UnexpectedProperty = {}));
|
|
313
281
|
|
|
@@ -328,21 +296,16 @@ const unboxReturnValue = ({
|
|
|
328
296
|
if (isPromise) {
|
|
329
297
|
return Promise.reject(value);
|
|
330
298
|
}
|
|
331
|
-
|
|
332
299
|
throw value;
|
|
333
300
|
}
|
|
334
|
-
|
|
335
301
|
if (isPromise) {
|
|
336
302
|
return Promise.reject(new Error(value));
|
|
337
303
|
}
|
|
338
|
-
|
|
339
304
|
throw new Error(value);
|
|
340
305
|
}
|
|
341
|
-
|
|
342
306
|
if (isPromise) {
|
|
343
307
|
return Promise.resolve(value);
|
|
344
308
|
}
|
|
345
|
-
|
|
346
309
|
return value;
|
|
347
310
|
};
|
|
348
311
|
|
|
@@ -350,16 +313,13 @@ const unboxReturnValue = ({
|
|
|
350
313
|
* An expectation repository with a configurable behavior for
|
|
351
314
|
* unexpected property access.
|
|
352
315
|
*/
|
|
353
|
-
|
|
354
316
|
class FlexibleRepository {
|
|
355
317
|
constructor(unexpectedProperty = exports.UnexpectedProperty.THROW) {
|
|
356
318
|
this.unexpectedProperty = void 0;
|
|
357
319
|
this.expectations = new Map();
|
|
358
320
|
this.expectedCallStats = new Map();
|
|
359
321
|
this.unexpectedCallStats = new Map();
|
|
360
|
-
|
|
361
322
|
this.apply = args => this.get(ApplyProp)(...args);
|
|
362
|
-
|
|
363
323
|
this.handlePropertyWithMatchingExpectations = (property, expectations) => {
|
|
364
324
|
// Avoid recording call stats for function calls, since the property is an
|
|
365
325
|
// internal detail.
|
|
@@ -368,63 +328,49 @@ class FlexibleRepository {
|
|
|
368
328
|
// function that will not match the given args.
|
|
369
329
|
this.recordExpected(property, undefined);
|
|
370
330
|
}
|
|
371
|
-
|
|
372
331
|
const propertyExpectation = expectations.find(e => e.expectation.matches(undefined));
|
|
373
|
-
|
|
374
332
|
if (propertyExpectation) {
|
|
375
333
|
this.countAndConsume(propertyExpectation);
|
|
376
334
|
return unboxReturnValue(propertyExpectation.expectation.returnValue);
|
|
377
335
|
}
|
|
378
|
-
|
|
379
336
|
return (...args) => {
|
|
380
337
|
const callExpectation = expectations.find(e => e.expectation.matches(args));
|
|
381
|
-
|
|
382
338
|
if (callExpectation) {
|
|
383
339
|
this.recordExpected(property, args);
|
|
384
340
|
this.countAndConsume(callExpectation);
|
|
385
341
|
return unboxReturnValue(callExpectation.expectation.returnValue);
|
|
386
342
|
}
|
|
387
|
-
|
|
388
343
|
return this.getValueForUnexpectedCall(property, args);
|
|
389
344
|
};
|
|
390
345
|
};
|
|
391
|
-
|
|
392
346
|
this.handlePropertyWithNoExpectations = property => {
|
|
393
347
|
switch (property) {
|
|
394
348
|
case 'toString':
|
|
395
349
|
return () => 'mock';
|
|
396
|
-
|
|
397
350
|
case '@@toStringTag':
|
|
398
351
|
case Symbol.toStringTag:
|
|
399
352
|
case 'name':
|
|
400
353
|
return 'mock';
|
|
401
354
|
// Promise.resolve() tries to see if it's a "thenable".
|
|
402
355
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#thenables
|
|
403
|
-
|
|
404
356
|
case 'then':
|
|
405
357
|
return undefined;
|
|
406
358
|
// pretty-format
|
|
407
|
-
|
|
408
359
|
case '$$typeof':
|
|
409
360
|
case 'constructor':
|
|
410
361
|
case '@@__IMMUTABLE_ITERABLE__@@':
|
|
411
362
|
case '@@__IMMUTABLE_RECORD__@@':
|
|
412
363
|
return null;
|
|
413
|
-
|
|
414
364
|
case MATCHER_SYMBOL:
|
|
415
365
|
return false;
|
|
416
|
-
|
|
417
366
|
case ApplyProp:
|
|
418
367
|
return (...args) => this.getValueForUnexpectedCall(property, args);
|
|
419
|
-
|
|
420
368
|
default:
|
|
421
369
|
return this.getValueForUnexpectedAccess(property);
|
|
422
370
|
}
|
|
423
371
|
};
|
|
424
|
-
|
|
425
372
|
this.unexpectedProperty = unexpectedProperty;
|
|
426
373
|
}
|
|
427
|
-
|
|
428
374
|
add(expectation) {
|
|
429
375
|
const {
|
|
430
376
|
property
|
|
@@ -435,89 +381,73 @@ class FlexibleRepository {
|
|
|
435
381
|
matchCount: 0
|
|
436
382
|
}]);
|
|
437
383
|
}
|
|
438
|
-
|
|
439
384
|
clear() {
|
|
440
385
|
this.expectations.clear();
|
|
441
386
|
this.expectedCallStats.clear();
|
|
442
387
|
this.unexpectedCallStats.clear();
|
|
443
388
|
}
|
|
444
|
-
|
|
445
389
|
// TODO: this returns any, but the interface returns unknown
|
|
446
390
|
// unknown causes errors in apply tests, and any causes bugs in bootstrapped SM
|
|
447
391
|
get(property) {
|
|
448
392
|
const expectations = this.expectations.get(property);
|
|
449
|
-
|
|
450
393
|
if (expectations && expectations.length) {
|
|
451
394
|
return this.handlePropertyWithMatchingExpectations(property, expectations);
|
|
452
395
|
}
|
|
453
|
-
|
|
454
396
|
return this.handlePropertyWithNoExpectations(property);
|
|
455
397
|
}
|
|
456
|
-
|
|
457
398
|
getAllProperties() {
|
|
458
399
|
return Array.from(this.expectations.keys());
|
|
459
400
|
}
|
|
460
|
-
|
|
461
401
|
getCallStats() {
|
|
462
402
|
return {
|
|
463
403
|
expected: this.expectedCallStats,
|
|
464
404
|
unexpected: this.unexpectedCallStats
|
|
465
405
|
};
|
|
466
406
|
}
|
|
467
|
-
|
|
468
407
|
getUnmet() {
|
|
469
408
|
return [].concat(...Array.from(this.expectations.values()).map(expectations => expectations.filter(e => e.expectation.min > e.matchCount).map(e => e.expectation)));
|
|
470
409
|
}
|
|
471
|
-
|
|
472
410
|
recordExpected(property, args) {
|
|
473
411
|
const calls = this.expectedCallStats.get(property) || [];
|
|
474
412
|
this.expectedCallStats.set(property, [...calls, {
|
|
475
413
|
arguments: args
|
|
476
414
|
}]);
|
|
477
415
|
}
|
|
478
|
-
|
|
479
416
|
recordUnexpected(property, args) {
|
|
480
417
|
const calls = this.unexpectedCallStats.get(property) || [];
|
|
481
418
|
this.unexpectedCallStats.set(property, [...calls, {
|
|
482
419
|
arguments: args
|
|
483
420
|
}]);
|
|
484
421
|
}
|
|
485
|
-
|
|
486
422
|
countAndConsume(expectation) {
|
|
487
423
|
// eslint-disable-next-line no-param-reassign
|
|
488
424
|
expectation.matchCount++;
|
|
489
425
|
this.consumeExpectation(expectation);
|
|
490
426
|
}
|
|
491
|
-
|
|
492
427
|
consumeExpectation(expectation) {
|
|
493
428
|
const {
|
|
494
429
|
property,
|
|
495
430
|
max
|
|
496
431
|
} = expectation.expectation;
|
|
497
432
|
const expectations = this.expectations.get(property);
|
|
498
|
-
|
|
499
433
|
if (expectation.matchCount === max) {
|
|
500
434
|
this.expectations.set(property, expectations.filter(e => e !== expectation));
|
|
501
435
|
}
|
|
502
436
|
}
|
|
503
|
-
|
|
504
437
|
getValueForUnexpectedCall(property, args) {
|
|
505
438
|
this.recordUnexpected(property, args);
|
|
506
439
|
throw new UnexpectedCall(property, args, this.getUnmet());
|
|
507
440
|
}
|
|
508
|
-
|
|
509
441
|
getValueForUnexpectedAccess(property) {
|
|
510
442
|
if (this.unexpectedProperty === exports.UnexpectedProperty.THROW) {
|
|
511
443
|
this.recordUnexpected(property, undefined);
|
|
512
444
|
throw new UnexpectedAccess(property, this.getUnmet());
|
|
513
445
|
}
|
|
514
|
-
|
|
515
446
|
return (...args) => {
|
|
516
447
|
this.recordUnexpected(property, args);
|
|
517
448
|
throw new UnexpectedCall(property, args, this.getUnmet());
|
|
518
449
|
};
|
|
519
450
|
}
|
|
520
|
-
|
|
521
451
|
}
|
|
522
452
|
|
|
523
453
|
/**
|
|
@@ -531,7 +461,6 @@ class FlexibleRepository {
|
|
|
531
461
|
* 23
|
|
532
462
|
* ).matches('bar', [1, 2, 3]) === true;
|
|
533
463
|
*/
|
|
534
|
-
|
|
535
464
|
class StrongExpectation {
|
|
536
465
|
constructor(property, args, returnValue, exactParams = false) {
|
|
537
466
|
this.property = void 0;
|
|
@@ -546,47 +475,37 @@ class StrongExpectation {
|
|
|
546
475
|
this.returnValue = returnValue;
|
|
547
476
|
this.exactParams = exactParams;
|
|
548
477
|
}
|
|
549
|
-
|
|
550
478
|
setInvocationCount(min, max = 1) {
|
|
551
479
|
this.min = min;
|
|
552
480
|
this.max = max;
|
|
553
481
|
}
|
|
554
|
-
|
|
555
482
|
matches(args) {
|
|
556
483
|
if (!this.matchesArgs(args)) {
|
|
557
484
|
return false;
|
|
558
485
|
}
|
|
559
|
-
|
|
560
486
|
this.matched++;
|
|
561
487
|
return this.max === 0 || this.matched <= this.max;
|
|
562
488
|
}
|
|
563
|
-
|
|
564
489
|
isUnmet() {
|
|
565
490
|
return this.matched < this.min;
|
|
566
491
|
}
|
|
567
|
-
|
|
568
492
|
matchesArgs(received) {
|
|
569
493
|
if (this.args === undefined) {
|
|
570
494
|
return !received;
|
|
571
495
|
}
|
|
572
|
-
|
|
573
496
|
if (!received) {
|
|
574
497
|
return false;
|
|
575
498
|
}
|
|
576
|
-
|
|
577
499
|
if (this.exactParams) {
|
|
578
500
|
if (this.args.length !== received.length) {
|
|
579
501
|
return false;
|
|
580
502
|
}
|
|
581
503
|
}
|
|
582
|
-
|
|
583
504
|
return this.args.every((arg, i) => arg.matches(received[i]));
|
|
584
505
|
}
|
|
585
|
-
|
|
586
506
|
toString() {
|
|
587
507
|
return printExpectation(this.property, this.args, this.returnValue, this.min, this.max);
|
|
588
508
|
}
|
|
589
|
-
|
|
590
509
|
}
|
|
591
510
|
|
|
592
511
|
class UnfinishedExpectation extends Error {
|
|
@@ -598,7 +517,6 @@ ${printWhen(property, args)}
|
|
|
598
517
|
Please finish it by setting a return value even if the value
|
|
599
518
|
is undefined.`);
|
|
600
519
|
}
|
|
601
|
-
|
|
602
520
|
}
|
|
603
521
|
class MissingWhen extends Error {
|
|
604
522
|
constructor() {
|
|
@@ -606,7 +524,6 @@ class MissingWhen extends Error {
|
|
|
606
524
|
|
|
607
525
|
Every call to set a return value must be preceded by an expectation.`);
|
|
608
526
|
}
|
|
609
|
-
|
|
610
527
|
}
|
|
611
528
|
class NotAMock extends Error {
|
|
612
529
|
constructor() {
|
|
@@ -614,7 +531,6 @@ class NotAMock extends Error {
|
|
|
614
531
|
|
|
615
532
|
Make sure you're passing in an actual mock.`);
|
|
616
533
|
}
|
|
617
|
-
|
|
618
534
|
}
|
|
619
535
|
class NestedWhen extends Error {
|
|
620
536
|
constructor(parentProp, childProp) {
|
|
@@ -631,7 +547,6 @@ You can return an object directly when the first property is accessed,
|
|
|
631
547
|
or you can even return a separate mock:
|
|
632
548
|
${snippet}`);
|
|
633
549
|
}
|
|
634
|
-
|
|
635
550
|
}
|
|
636
551
|
|
|
637
552
|
class ExpectationBuilderWithFactory {
|
|
@@ -645,59 +560,33 @@ class ExpectationBuilderWithFactory {
|
|
|
645
560
|
this.concreteMatcher = concreteMatcher;
|
|
646
561
|
this.exactParams = exactParams;
|
|
647
562
|
}
|
|
648
|
-
|
|
649
563
|
setProperty(value) {
|
|
650
564
|
if (this.property) {
|
|
651
565
|
throw new UnfinishedExpectation(this.property, this.args);
|
|
652
566
|
}
|
|
653
|
-
|
|
654
567
|
this.property = value;
|
|
655
568
|
}
|
|
656
|
-
|
|
657
569
|
setArgs(value) {
|
|
658
570
|
this.args = value;
|
|
659
571
|
}
|
|
660
|
-
|
|
661
572
|
finish(returnValue) {
|
|
662
573
|
if (!this.property) {
|
|
663
574
|
throw new MissingWhen();
|
|
664
575
|
}
|
|
665
|
-
|
|
666
576
|
const expectation = this.createExpectation(this.property, this.args, returnValue, this.concreteMatcher, this.exactParams);
|
|
667
577
|
this.property = undefined;
|
|
668
578
|
this.args = undefined;
|
|
669
579
|
return expectation;
|
|
670
580
|
}
|
|
671
|
-
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
function _extends() {
|
|
675
|
-
_extends = Object.assign || function (target) {
|
|
676
|
-
for (var i = 1; i < arguments.length; i++) {
|
|
677
|
-
var source = arguments[i];
|
|
678
|
-
|
|
679
|
-
for (var key in source) {
|
|
680
|
-
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
681
|
-
target[key] = source[key];
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
return target;
|
|
687
|
-
};
|
|
688
|
-
|
|
689
|
-
return _extends.apply(this, arguments);
|
|
690
581
|
}
|
|
691
582
|
|
|
692
583
|
const removeUndefined = object => {
|
|
693
584
|
if (Array.isArray(object)) {
|
|
694
585
|
return object.map(x => removeUndefined(x));
|
|
695
586
|
}
|
|
696
|
-
|
|
697
587
|
if (!lodash.isObjectLike(object)) {
|
|
698
588
|
return object;
|
|
699
589
|
}
|
|
700
|
-
|
|
701
590
|
return lodash.omitBy(object, lodash.isUndefined);
|
|
702
591
|
};
|
|
703
592
|
/**
|
|
@@ -709,18 +598,15 @@ const removeUndefined = object => {
|
|
|
709
598
|
* non `Object` instances with different constructors as not equal. Setting
|
|
710
599
|
* this to `false` will consider the objects in both cases as equal.
|
|
711
600
|
*
|
|
712
|
-
* @see {@link It.
|
|
601
|
+
* @see {@link It.containsObject} or {@link It.isArray} if you want to nest matchers.
|
|
713
602
|
* @see {@link It.is} if you want to use strict equality.
|
|
714
603
|
*/
|
|
715
|
-
|
|
716
|
-
|
|
717
604
|
const deepEquals = (expected, {
|
|
718
605
|
strict = true
|
|
719
606
|
} = {}) => matches(actual => {
|
|
720
607
|
if (strict) {
|
|
721
608
|
return lodash.isEqual(actual, expected);
|
|
722
609
|
}
|
|
723
|
-
|
|
724
610
|
return lodash.isEqual(removeUndefined(actual), removeUndefined(expected));
|
|
725
611
|
}, {
|
|
726
612
|
toString: () => printValue(expected),
|
|
@@ -743,9 +629,11 @@ let currentDefaults = defaults;
|
|
|
743
629
|
* calls don't stack e.g. calling this with `{}` will clear any previously
|
|
744
630
|
* applied defaults.
|
|
745
631
|
*/
|
|
746
|
-
|
|
747
632
|
const setDefaults = newDefaults => {
|
|
748
|
-
currentDefaults =
|
|
633
|
+
currentDefaults = {
|
|
634
|
+
...defaults,
|
|
635
|
+
...newDefaults
|
|
636
|
+
};
|
|
749
637
|
};
|
|
750
638
|
|
|
751
639
|
/**
|
|
@@ -763,7 +651,6 @@ const setDefaults = newDefaults => {
|
|
|
763
651
|
* For that reason we can't just store the currently active mock, but also
|
|
764
652
|
* whether we finished the expectation or not.
|
|
765
653
|
*/
|
|
766
|
-
|
|
767
654
|
let activeMock;
|
|
768
655
|
const setActiveMock = mock => {
|
|
769
656
|
activeMock = mock;
|
|
@@ -775,13 +662,11 @@ const getActiveMock = () => activeMock;
|
|
|
775
662
|
* This is needed because we can't reliably pass the state between `when`
|
|
776
663
|
* and `thenReturn`.
|
|
777
664
|
*/
|
|
778
|
-
|
|
779
665
|
const mockMap = new Map();
|
|
780
666
|
const getMockState = mock => {
|
|
781
667
|
if (mockMap.has(mock)) {
|
|
782
668
|
return mockMap.get(mock);
|
|
783
669
|
}
|
|
784
|
-
|
|
785
670
|
throw new NotAMock();
|
|
786
671
|
};
|
|
787
672
|
const setMockState = (mock, state) => {
|
|
@@ -789,43 +674,35 @@ const setMockState = (mock, state) => {
|
|
|
789
674
|
};
|
|
790
675
|
const getAllMocks = () => Array.from(mockMap.entries());
|
|
791
676
|
|
|
792
|
-
const createProxy = traps =>
|
|
677
|
+
const createProxy = traps =>
|
|
678
|
+
// The Proxy target MUST be a function, otherwise we can't use the `apply` trap:
|
|
793
679
|
// https://262.ecma-international.org/6.0/#sec-proxy-object-internal-methods-and-internal-slots-call-thisargument-argumentslist
|
|
794
680
|
// eslint-disable-next-line no-empty-function,@typescript-eslint/no-empty-function
|
|
795
|
-
new Proxy(
|
|
796
|
-
/* istanbul ignore next */
|
|
797
|
-
() => {}, {
|
|
681
|
+
new Proxy( /* istanbul ignore next */() => {}, {
|
|
798
682
|
get: (target, prop) => {
|
|
799
683
|
if (prop === 'bind') {
|
|
800
684
|
return (thisArg, ...args) => (...moreArgs) => traps.apply([...args, ...moreArgs]);
|
|
801
685
|
}
|
|
802
|
-
|
|
803
686
|
if (prop === 'apply') {
|
|
804
687
|
return (thisArg, args) => traps.apply(args || []);
|
|
805
688
|
}
|
|
806
|
-
|
|
807
689
|
if (prop === 'call') {
|
|
808
690
|
return (thisArg, ...args) => traps.apply(args);
|
|
809
691
|
}
|
|
810
|
-
|
|
811
692
|
return traps.property(prop);
|
|
812
693
|
},
|
|
813
694
|
apply: (target, thisArg, args) => traps.apply(args),
|
|
814
695
|
ownKeys: () => traps.ownKeys(),
|
|
815
|
-
|
|
816
696
|
getOwnPropertyDescriptor(target, prop) {
|
|
817
697
|
const keys = traps.ownKeys();
|
|
818
|
-
|
|
819
698
|
if (keys.includes(prop)) {
|
|
820
699
|
return {
|
|
821
700
|
configurable: true,
|
|
822
701
|
enumerable: true
|
|
823
702
|
};
|
|
824
703
|
}
|
|
825
|
-
|
|
826
704
|
return undefined;
|
|
827
705
|
}
|
|
828
|
-
|
|
829
706
|
});
|
|
830
707
|
|
|
831
708
|
const createStub = (repo, builder, getCurrentMode) => {
|
|
@@ -834,7 +711,6 @@ const createStub = (repo, builder, getCurrentMode) => {
|
|
|
834
711
|
if (getCurrentMode() === Mode.CALL) {
|
|
835
712
|
return repo.get(property);
|
|
836
713
|
}
|
|
837
|
-
|
|
838
714
|
setActiveMock(stub);
|
|
839
715
|
builder.setProperty(property);
|
|
840
716
|
return createProxy({
|
|
@@ -853,7 +729,6 @@ const createStub = (repo, builder, getCurrentMode) => {
|
|
|
853
729
|
if (getCurrentMode() === Mode.CALL) {
|
|
854
730
|
return repo.apply(args);
|
|
855
731
|
}
|
|
856
|
-
|
|
857
732
|
setActiveMock(stub);
|
|
858
733
|
builder.setProperty(ApplyProp);
|
|
859
734
|
builder.setArgs(args);
|
|
@@ -863,7 +738,6 @@ const createStub = (repo, builder, getCurrentMode) => {
|
|
|
863
738
|
if (getCurrentMode() === Mode.CALL) {
|
|
864
739
|
return repo.getAllProperties();
|
|
865
740
|
}
|
|
866
|
-
|
|
867
741
|
throw new Error('Spreading during an expectation is not supported.');
|
|
868
742
|
}
|
|
869
743
|
});
|
|
@@ -872,19 +746,15 @@ const createStub = (repo, builder, getCurrentMode) => {
|
|
|
872
746
|
|
|
873
747
|
const strongExpectationFactory = (property, args, returnValue, concreteMatcher, exactParams) => new StrongExpectation(property, // Wrap every non-matcher in the default matcher.
|
|
874
748
|
args == null ? void 0 : args.map(arg => isMatcher(arg) ? arg : concreteMatcher(arg)), returnValue, exactParams);
|
|
875
|
-
|
|
876
749
|
var Mode;
|
|
877
|
-
|
|
878
750
|
(function (Mode) {
|
|
879
751
|
Mode[Mode["EXPECT"] = 0] = "EXPECT";
|
|
880
752
|
Mode[Mode["CALL"] = 1] = "CALL";
|
|
881
753
|
})(Mode || (Mode = {}));
|
|
882
|
-
|
|
883
754
|
let currentMode = Mode.CALL;
|
|
884
755
|
const setMode = mode => {
|
|
885
756
|
currentMode = mode;
|
|
886
757
|
};
|
|
887
|
-
|
|
888
758
|
const getMode = () => currentMode;
|
|
889
759
|
/**
|
|
890
760
|
* Create a type safe mock.
|
|
@@ -907,8 +777,6 @@ const getMode = () => currentMode;
|
|
|
907
777
|
*
|
|
908
778
|
* fn() === 23;
|
|
909
779
|
*/
|
|
910
|
-
|
|
911
|
-
|
|
912
780
|
const mock = ({
|
|
913
781
|
unexpectedProperty,
|
|
914
782
|
concreteMatcher,
|
|
@@ -934,39 +802,31 @@ const createInvocationCount = expectation => ({
|
|
|
934
802
|
between(min, max) {
|
|
935
803
|
expectation.setInvocationCount(min, max);
|
|
936
804
|
},
|
|
937
|
-
|
|
938
805
|
/* istanbul ignore next */
|
|
939
806
|
times(exact) {
|
|
940
807
|
expectation.setInvocationCount(exact, exact);
|
|
941
808
|
},
|
|
942
|
-
|
|
943
809
|
/* istanbul ignore next */
|
|
944
810
|
anyTimes() {
|
|
945
811
|
expectation.setInvocationCount(0, 0);
|
|
946
812
|
},
|
|
947
|
-
|
|
948
813
|
/* istanbul ignore next */
|
|
949
814
|
atLeast(min) {
|
|
950
815
|
expectation.setInvocationCount(min, Infinity);
|
|
951
816
|
},
|
|
952
|
-
|
|
953
817
|
/* istanbul ignore next */
|
|
954
818
|
atMost(max) {
|
|
955
819
|
expectation.setInvocationCount(0, max);
|
|
956
820
|
},
|
|
957
|
-
|
|
958
821
|
/* istanbul ignore next */
|
|
959
822
|
once() {
|
|
960
823
|
expectation.setInvocationCount(1, 1);
|
|
961
824
|
},
|
|
962
|
-
|
|
963
825
|
/* istanbul ignore next */
|
|
964
826
|
twice() {
|
|
965
827
|
expectation.setInvocationCount(2, 2);
|
|
966
828
|
}
|
|
967
829
|
/* eslint-enable no-param-reassign, no-multi-assign */
|
|
968
|
-
|
|
969
|
-
|
|
970
830
|
});
|
|
971
831
|
|
|
972
832
|
const finishExpectation = (returnValue, builder, repo) => {
|
|
@@ -974,21 +834,18 @@ const finishExpectation = (returnValue, builder, repo) => {
|
|
|
974
834
|
repo.add(finishedExpectation);
|
|
975
835
|
return createInvocationCount(finishedExpectation);
|
|
976
836
|
};
|
|
977
|
-
|
|
978
837
|
const getError = errorOrMessage => {
|
|
979
838
|
if (typeof errorOrMessage === 'string') {
|
|
980
839
|
return new Error(errorOrMessage);
|
|
981
840
|
}
|
|
982
|
-
|
|
983
841
|
if (errorOrMessage instanceof Error) {
|
|
984
842
|
return errorOrMessage;
|
|
985
843
|
}
|
|
986
|
-
|
|
987
844
|
return new Error();
|
|
988
845
|
};
|
|
989
|
-
|
|
990
846
|
const createReturns = (builder, repository) => ({
|
|
991
|
-
thenReturn: returnValue => finishExpectation(
|
|
847
|
+
thenReturn: returnValue => finishExpectation(
|
|
848
|
+
// This will handle both thenReturn(23) and thenReturn(Promise.resolve(3)).
|
|
992
849
|
{
|
|
993
850
|
value: returnValue,
|
|
994
851
|
isError: false,
|
|
@@ -1036,7 +893,6 @@ const createReturns = (builder, repository) => ({
|
|
|
1036
893
|
* const fn = mock<(x: number) => Promise<number>();
|
|
1037
894
|
* when(() => fn(23)).thenResolve(42);
|
|
1038
895
|
*/
|
|
1039
|
-
|
|
1040
896
|
const when = expectation => {
|
|
1041
897
|
setMode(Mode.EXPECT);
|
|
1042
898
|
expectation();
|
|
@@ -1060,7 +916,6 @@ const when = expectation => {
|
|
|
1060
916
|
*
|
|
1061
917
|
* fn(); // throws
|
|
1062
918
|
*/
|
|
1063
|
-
|
|
1064
919
|
const reset = mock => {
|
|
1065
920
|
getMockState(mock).repository.clear();
|
|
1066
921
|
};
|
|
@@ -1069,7 +924,6 @@ const reset = mock => {
|
|
|
1069
924
|
*
|
|
1070
925
|
* @see reset
|
|
1071
926
|
*/
|
|
1072
|
-
|
|
1073
927
|
const resetAll = () => {
|
|
1074
928
|
getAllMocks().forEach(([mock]) => {
|
|
1075
929
|
reset(mock);
|
|
@@ -1082,7 +936,6 @@ class UnmetExpectations extends Error {
|
|
|
1082
936
|
|
|
1083
937
|
- ${expectations.map(e => e.toString()).join('\n - ')}`));
|
|
1084
938
|
}
|
|
1085
|
-
|
|
1086
939
|
}
|
|
1087
940
|
/**
|
|
1088
941
|
* Merge property accesses and method calls for the same property
|
|
@@ -1092,18 +945,14 @@ class UnmetExpectations extends Error {
|
|
|
1092
945
|
* mergeCalls({ getData: [{ arguments: undefined }, { arguments: [1, 2, 3] }] }
|
|
1093
946
|
* // returns { getData: [{ arguments: [1, 2, 3] } }
|
|
1094
947
|
*/
|
|
1095
|
-
|
|
1096
948
|
const mergeCalls = callMap => new Map(Array.from(callMap.entries()).map(([property, calls]) => {
|
|
1097
949
|
const hasMethodCalls = calls.some(call => call.arguments);
|
|
1098
950
|
const hasPropertyAccesses = calls.some(call => !call.arguments);
|
|
1099
|
-
|
|
1100
951
|
if (hasMethodCalls && hasPropertyAccesses) {
|
|
1101
952
|
return [property, calls.filter(call => call.arguments)];
|
|
1102
953
|
}
|
|
1103
|
-
|
|
1104
954
|
return [property, calls];
|
|
1105
955
|
}));
|
|
1106
|
-
|
|
1107
956
|
class UnexpectedCalls extends Error {
|
|
1108
957
|
constructor(unexpectedCalls, expectations) {
|
|
1109
958
|
const printedCalls = Array.from(mergeCalls(unexpectedCalls).entries()).map(([property, calls]) => calls.map(call => printCall(property, call.arguments)).join('\n - ')).join('\n - ');
|
|
@@ -1113,18 +962,14 @@ class UnexpectedCalls extends Error {
|
|
|
1113
962
|
|
|
1114
963
|
${printRemainingExpectations(expectations)}`));
|
|
1115
964
|
}
|
|
1116
|
-
|
|
1117
965
|
}
|
|
1118
966
|
|
|
1119
967
|
const verifyRepo = repository => {
|
|
1120
968
|
const unmetExpectations = repository.getUnmet();
|
|
1121
|
-
|
|
1122
969
|
if (unmetExpectations.length) {
|
|
1123
970
|
throw new UnmetExpectations(unmetExpectations);
|
|
1124
971
|
}
|
|
1125
|
-
|
|
1126
972
|
const callStats = repository.getCallStats();
|
|
1127
|
-
|
|
1128
973
|
if (callStats.unexpected.size) {
|
|
1129
974
|
throw new UnexpectedCalls(callStats.unexpected, unmetExpectations);
|
|
1130
975
|
}
|
|
@@ -1146,7 +991,6 @@ const verifyRepo = repository => {
|
|
|
1146
991
|
*
|
|
1147
992
|
* verify(fn); // throws
|
|
1148
993
|
*/
|
|
1149
|
-
|
|
1150
994
|
const verify = mock => {
|
|
1151
995
|
const {
|
|
1152
996
|
repository
|
|
@@ -1158,7 +1002,6 @@ const verify = mock => {
|
|
|
1158
1002
|
*
|
|
1159
1003
|
* @see verify
|
|
1160
1004
|
*/
|
|
1161
|
-
|
|
1162
1005
|
const verifyAll = () => {
|
|
1163
1006
|
getAllMocks().forEach(([mock]) => {
|
|
1164
1007
|
verify(mock);
|
|
@@ -1172,7 +1015,6 @@ const verifyAll = () => {
|
|
|
1172
1015
|
*
|
|
1173
1016
|
* @see It.deepEquals A matcher that uses deep equality.
|
|
1174
1017
|
*/
|
|
1175
|
-
|
|
1176
1018
|
const is = expected => matches(actual => Object.is(actual, expected), {
|
|
1177
1019
|
toString: () => `${printValue(expected)}`,
|
|
1178
1020
|
getDiff: actual => ({
|
|
@@ -1190,7 +1032,6 @@ const is = expected => matches(actual => Object.is(actual, expected), {
|
|
|
1190
1032
|
*
|
|
1191
1033
|
* fn(23, 'foobar') === 1
|
|
1192
1034
|
*/
|
|
1193
|
-
|
|
1194
1035
|
const isAny = () => matches(() => true, {
|
|
1195
1036
|
toString: () => 'Matcher<any>'
|
|
1196
1037
|
});
|
|
@@ -1215,21 +1056,17 @@ const isAny = () => matches(() => true, {
|
|
|
1215
1056
|
* @example
|
|
1216
1057
|
* It.isArray([It.isString({ containing: 'foobar' })])
|
|
1217
1058
|
*/
|
|
1218
|
-
|
|
1219
1059
|
const isArray = containing => matches(actual => {
|
|
1220
1060
|
if (!Array.isArray(actual)) {
|
|
1221
1061
|
return false;
|
|
1222
1062
|
}
|
|
1223
|
-
|
|
1224
1063
|
if (!containing) {
|
|
1225
1064
|
return true;
|
|
1226
1065
|
}
|
|
1227
|
-
|
|
1228
1066
|
return containing.every(x => actual.find(y => {
|
|
1229
1067
|
if (isMatcher(x)) {
|
|
1230
1068
|
return x.matches(y);
|
|
1231
1069
|
}
|
|
1232
|
-
|
|
1233
1070
|
return deepEquals(x).matches(y);
|
|
1234
1071
|
}) !== undefined);
|
|
1235
1072
|
}, {
|
|
@@ -1242,12 +1079,10 @@ const isArray = containing => matches(actual => {
|
|
|
1242
1079
|
if (isMatcher(value)) {
|
|
1243
1080
|
return value.toString();
|
|
1244
1081
|
}
|
|
1245
|
-
|
|
1246
1082
|
return value;
|
|
1247
1083
|
}).join(', ')}])`
|
|
1248
1084
|
};
|
|
1249
1085
|
}
|
|
1250
|
-
|
|
1251
1086
|
return {
|
|
1252
1087
|
actual: `${printValue(actual)} (${typeof actual})`,
|
|
1253
1088
|
expected: 'Matcher<array>'
|
|
@@ -1265,7 +1100,6 @@ const isArray = containing => matches(actual => {
|
|
|
1265
1100
|
* fn(20.5) === 42
|
|
1266
1101
|
* fn(NaN) // throws
|
|
1267
1102
|
*/
|
|
1268
|
-
|
|
1269
1103
|
const isNumber = () => matches(actual => typeof actual === 'number' && !Number.isNaN(actual), {
|
|
1270
1104
|
toString: () => 'Matcher<number>',
|
|
1271
1105
|
getDiff: actual => ({
|
|
@@ -1278,7 +1112,7 @@ const isNumber = () => matches(actual => typeof actual === 'number' && !Number.i
|
|
|
1278
1112
|
* Matches any plain object e.g. object literals or objects created with `Object.create()`.
|
|
1279
1113
|
*
|
|
1280
1114
|
* Classes, arrays, maps, sets etc. are not considered plain objects.
|
|
1281
|
-
* You can use {@link
|
|
1115
|
+
* You can use {@link containsObject} or {@link matches} to match those.
|
|
1282
1116
|
*
|
|
1283
1117
|
* @example
|
|
1284
1118
|
* const fn = mock<({ foo: string }) => number>();
|
|
@@ -1286,7 +1120,6 @@ const isNumber = () => matches(actual => typeof actual === 'number' && !Number.i
|
|
|
1286
1120
|
*
|
|
1287
1121
|
* fn({ foo: 'bar' }) // returns 42
|
|
1288
1122
|
*/
|
|
1289
|
-
|
|
1290
1123
|
const isPlainObject = () => matches(actual => lodash.isPlainObject(actual), {
|
|
1291
1124
|
toString: () => 'Matcher<object>',
|
|
1292
1125
|
getDiff: actual => {
|
|
@@ -1299,89 +1132,68 @@ const isPlainObject = () => matches(actual => lodash.isPlainObject(actual), {
|
|
|
1299
1132
|
});
|
|
1300
1133
|
|
|
1301
1134
|
const looksLikeObject = value => lodash.isPlainObject(value);
|
|
1302
|
-
|
|
1303
1135
|
const getExpectedObjectDiff = (actual, expected) => Object.fromEntries(getKeys(expected).map(key => {
|
|
1304
1136
|
const expectedValue = getKey(expected, key);
|
|
1305
1137
|
const actualValue = getKey(actual, key);
|
|
1306
|
-
|
|
1307
1138
|
if (isMatcher(expectedValue)) {
|
|
1308
1139
|
return [key, expectedValue.getDiff(actualValue).expected];
|
|
1309
1140
|
}
|
|
1310
|
-
|
|
1311
1141
|
if (looksLikeObject(expectedValue)) {
|
|
1312
1142
|
return [key, getExpectedObjectDiff(actualValue, expectedValue)];
|
|
1313
1143
|
}
|
|
1314
|
-
|
|
1315
1144
|
return [key, expectedValue];
|
|
1316
1145
|
}));
|
|
1317
|
-
|
|
1318
1146
|
const getActualObjectDiff = (actual, expected) => {
|
|
1319
1147
|
const actualKeys = getKeys(actual);
|
|
1320
1148
|
const expectedKeys = new Set(getKeys(expected));
|
|
1321
1149
|
const commonKeys = actualKeys.filter(key => expectedKeys.has(key));
|
|
1322
|
-
|
|
1323
1150
|
if (!commonKeys.length) {
|
|
1324
1151
|
// When we don't have any common keys we return the whole object
|
|
1325
1152
|
// so the user can inspect what's in there.
|
|
1326
1153
|
return actual;
|
|
1327
1154
|
}
|
|
1328
|
-
|
|
1329
1155
|
return Object.fromEntries(commonKeys.map(key => {
|
|
1330
1156
|
const expectedValue = getKey(expected, key);
|
|
1331
1157
|
const actualValue = getKey(actual, key);
|
|
1332
|
-
|
|
1333
1158
|
if (isMatcher(expectedValue)) {
|
|
1334
1159
|
return [key, expectedValue.getDiff(actualValue).actual];
|
|
1335
1160
|
}
|
|
1336
|
-
|
|
1337
1161
|
if (looksLikeObject(expectedValue)) {
|
|
1338
1162
|
return [key, getActualObjectDiff(actualValue, expectedValue)];
|
|
1339
1163
|
}
|
|
1340
|
-
|
|
1341
1164
|
return [key, actualValue];
|
|
1342
1165
|
}));
|
|
1343
1166
|
};
|
|
1344
|
-
|
|
1345
1167
|
const getKeys = value => {
|
|
1346
1168
|
if (typeof value === 'object' && value !== null) {
|
|
1347
1169
|
return Reflect.ownKeys(value);
|
|
1348
1170
|
}
|
|
1349
|
-
|
|
1350
1171
|
return [];
|
|
1351
1172
|
};
|
|
1352
|
-
|
|
1353
1173
|
const getKey = (value, key) => // @ts-expect-error because we're fine with a runtime undefined value
|
|
1354
1174
|
value == null ? void 0 : value[key];
|
|
1355
|
-
|
|
1356
1175
|
const isMatch = (actual, expected) => {
|
|
1357
1176
|
const actualKeys = getKeys(actual);
|
|
1358
1177
|
const expectedKeys = getKeys(expected);
|
|
1359
|
-
|
|
1360
1178
|
if (!isArray(expectedKeys).matches(actualKeys)) {
|
|
1361
1179
|
return false;
|
|
1362
1180
|
}
|
|
1363
|
-
|
|
1364
1181
|
return expectedKeys.every(key => {
|
|
1365
1182
|
const expectedValue = getKey(expected, key);
|
|
1366
1183
|
const actualValue = getKey(actual, key);
|
|
1367
|
-
|
|
1368
1184
|
if (isMatcher(expectedValue)) {
|
|
1369
1185
|
return expectedValue.matches(actualValue);
|
|
1370
1186
|
}
|
|
1371
|
-
|
|
1372
1187
|
if (looksLikeObject(expectedValue)) {
|
|
1373
1188
|
return isMatch(actualValue, expectedValue);
|
|
1374
1189
|
}
|
|
1375
|
-
|
|
1376
1190
|
return deepEquals(expectedValue).matches(actualValue);
|
|
1377
1191
|
});
|
|
1378
1192
|
};
|
|
1379
|
-
|
|
1380
1193
|
const deepPrintObject = value => lodash.cloneDeepWith(value, value => {
|
|
1381
1194
|
if (isMatcher(value)) {
|
|
1382
1195
|
return value.toString();
|
|
1383
1196
|
}
|
|
1384
|
-
|
|
1385
1197
|
return undefined;
|
|
1386
1198
|
});
|
|
1387
1199
|
/**
|
|
@@ -1391,21 +1203,22 @@ const deepPrintObject = value => lodash.cloneDeepWith(value, value => {
|
|
|
1391
1203
|
* @param partial A subset of the expected object that will be recursively matched.
|
|
1392
1204
|
* Supports nested matchers.
|
|
1393
1205
|
* Concrete values will be compared with {@link deepEquals}.
|
|
1394
|
-
*
|
|
1395
|
-
*
|
|
1206
|
+
*
|
|
1207
|
+
* @see {@link isPlainObject} if you want to match any plain object.
|
|
1396
1208
|
*
|
|
1397
1209
|
* @example
|
|
1398
1210
|
* const fn = mock<(pos: { x: number, y: number }) => number>();
|
|
1399
|
-
* when(() => fn(It.
|
|
1211
|
+
* when(() => fn(It.containsObject({ x: 23 }))).returns(42);
|
|
1400
1212
|
*
|
|
1401
1213
|
* fn({ x: 23, y: 200 }) // returns 42
|
|
1402
1214
|
*
|
|
1403
1215
|
* @example
|
|
1404
|
-
* It.
|
|
1216
|
+
* It.containsObject({ foo: It.isString() })
|
|
1405
1217
|
*/
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1218
|
+
// T is not constrained to ObjectType because of
|
|
1219
|
+
// https://github.com/microsoft/TypeScript/issues/57810,
|
|
1220
|
+
// but K is to avoid inferring non-object partials
|
|
1221
|
+
const containsObject = partial => matches(actual => isMatch(actual, partial), {
|
|
1409
1222
|
toString: () => `Matcher<object>(${printValue(deepPrintObject(partial))})`,
|
|
1410
1223
|
getDiff: actual => ({
|
|
1411
1224
|
actual: getActualObjectDiff(actual, partial),
|
|
@@ -1426,27 +1239,22 @@ const isPartial = partial => matches(actual => isMatch(actual, partial), {
|
|
|
1426
1239
|
* fn('foo', 'baz') // throws
|
|
1427
1240
|
* fn('foo', 'bar') === 42
|
|
1428
1241
|
*/
|
|
1429
|
-
|
|
1430
1242
|
const isString = matching => matches(actual => {
|
|
1431
1243
|
if (typeof actual !== 'string') {
|
|
1432
1244
|
return false;
|
|
1433
1245
|
}
|
|
1434
|
-
|
|
1435
1246
|
if (!matching) {
|
|
1436
1247
|
return true;
|
|
1437
1248
|
}
|
|
1438
|
-
|
|
1439
1249
|
if (typeof matching === 'string') {
|
|
1440
1250
|
return actual.indexOf(matching) !== -1;
|
|
1441
1251
|
}
|
|
1442
|
-
|
|
1443
1252
|
return matching.test(actual);
|
|
1444
1253
|
}, {
|
|
1445
1254
|
toString: () => {
|
|
1446
1255
|
if (matching) {
|
|
1447
1256
|
return `Matcher<string>(${matching})`;
|
|
1448
1257
|
}
|
|
1449
|
-
|
|
1450
1258
|
return 'Matcher<string>';
|
|
1451
1259
|
},
|
|
1452
1260
|
getDiff: actual => {
|
|
@@ -1456,7 +1264,6 @@ const isString = matching => matches(actual => {
|
|
|
1456
1264
|
actual
|
|
1457
1265
|
};
|
|
1458
1266
|
}
|
|
1459
|
-
|
|
1460
1267
|
return {
|
|
1461
1268
|
expected: 'Matcher<string>',
|
|
1462
1269
|
actual: `${actual} (${typeof actual})`
|
|
@@ -1481,7 +1288,6 @@ const isString = matching => matches(actual => {
|
|
|
1481
1288
|
* fn(x => x + 1);
|
|
1482
1289
|
* matcher.value?.(3) === 4
|
|
1483
1290
|
*/
|
|
1484
|
-
|
|
1485
1291
|
const willCapture = name => {
|
|
1486
1292
|
let capturedValue;
|
|
1487
1293
|
const matcher = {
|
|
@@ -1495,11 +1301,9 @@ const willCapture = name => {
|
|
|
1495
1301
|
actual,
|
|
1496
1302
|
expected: actual
|
|
1497
1303
|
}),
|
|
1498
|
-
|
|
1499
1304
|
get value() {
|
|
1500
1305
|
return capturedValue;
|
|
1501
1306
|
}
|
|
1502
|
-
|
|
1503
1307
|
};
|
|
1504
1308
|
return matcher;
|
|
1505
1309
|
};
|
|
@@ -1514,7 +1318,7 @@ var it = {
|
|
|
1514
1318
|
isArray: isArray,
|
|
1515
1319
|
isNumber: isNumber,
|
|
1516
1320
|
isPlainObject: isPlainObject,
|
|
1517
|
-
|
|
1321
|
+
containsObject: containsObject,
|
|
1518
1322
|
isString: isString,
|
|
1519
1323
|
matches: matches,
|
|
1520
1324
|
willCapture: willCapture
|