Skip to content
Permalink
Newer
Older
100644 963 lines (842 sloc) 30.1 KB
1
/**
2
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
4
* This source code is licensed under the MIT license found in the
5
* LICENSE file in the root directory of this source tree.
6
*
7
*/
8
9
/* eslint-disable local/ban-types-eventually */
10
11
import {
12
arrayBufferEquality,
13
equals,
14
getObjectSubset,
15
getPath,
16
iterableEquality,
17
pathAsArray,
18
sparseArrayEquality,
19
subsetEquality,
20
typeEquality,
21
} from '@jest/expect-utils';
22
import {getType, isPrimitive} from 'jest-get-type';
25
EXPECTED_COLOR,
26
MatcherHintOptions,
27
RECEIVED_COLOR,
28
SUGGEST_TO_CONTAIN_EQUAL,
29
ensureExpectedIsNonNegativeInteger,
30
ensureNoExpected,
31
ensureNumbers,
34
matcherHint,
36
printExpected,
37
printReceived,
40
} from 'jest-matcher-utils';
43
printExpectedConstructorName,
44
printExpectedConstructorNameNot,
45
printReceivedArrayContainExpectedItem,
46
printReceivedConstructorName,
47
printReceivedConstructorNameNot,
48
printReceivedStringContainExpectedResult,
49
printReceivedStringContainExpectedSubstring,
50
} from './print';
51
import type {MatchersObject} from './types';
53
// Omit colon and one or more spaces, so can call getLabelPrinter.
54
const EXPECTED_LABEL = 'Expected';
55
const RECEIVED_LABEL = 'Received';
56
const EXPECTED_VALUE_LABEL = 'Expected value';
57
const RECEIVED_VALUE_LABEL = 'Received value';
59
// The optional property of matcher context is true if undefined.
60
const isExpand = (expand?: boolean): boolean => expand !== false;
61
62
const toStrictEqualTesters = [
63
iterableEquality,
64
typeEquality,
65
sparseArrayEquality,
69
type ContainIterable =
70
| Array<unknown>
71
| Set<unknown>
72
| NodeListOf<Node>
73
| DOMTokenList
74
| HTMLCollectionOf<any>;
76
const matchers: MatchersObject = {
77
toBe(received: unknown, expected: unknown) {
78
const matcherName = 'toBe';
79
const options: MatcherHintOptions = {
80
comment: 'Object.is equality',
81
isNot: this.isNot,
85
const pass = Object.is(received, expected);
86
87
const message = pass
88
? () =>
89
// eslint-disable-next-line prefer-template
90
matcherHint(matcherName, undefined, undefined, options) +
91
'\n\n' +
92
`Expected: not ${printExpected(expected)}`
93
: () => {
94
const expectedType = getType(expected);
95
96
let deepEqualityName = null;
97
if (expectedType !== 'map' && expectedType !== 'set') {
98
// If deep equality passes when referential identity fails,
99
// but exclude map and set until review of their equality logic.
100
if (equals(received, expected, toStrictEqualTesters, true)) {
101
deepEqualityName = 'toStrictEqual';
102
} else if (equals(received, expected, [iterableEquality])) {
103
deepEqualityName = 'toEqual';
104
}
105
}
106
107
return (
108
// eslint-disable-next-line prefer-template
109
matcherHint(matcherName, undefined, undefined, options) +
110
'\n\n' +
111
(deepEqualityName !== null
112
? `${DIM_COLOR(
113
`If it should pass with deep equality, replace "${matcherName}" with "${deepEqualityName}"`,
116
printDiffOrStringify(
117
expected,
118
received,
119
EXPECTED_LABEL,
120
RECEIVED_LABEL,
124
};
126
// Passing the actual and expected objects so that a custom reporter
127
// could access them, for example in order to display a custom visual diff,
128
// or create a different error message
129
return {actual: received, expected, message, name: matcherName, pass};
130
},
132
toBeCloseTo(received: number, expected: number, precision = 2) {
133
const matcherName = 'toBeCloseTo';
134
const secondArgument = arguments.length === 3 ? 'precision' : undefined;
136
const options: MatcherHintOptions = {
138
promise: this.promise,
139
secondArgument,
140
secondArgumentColor: (arg: string) => arg,
142
143
if (typeof expected !== 'number') {
144
throw new Error(
145
matcherErrorMessage(
146
matcherHint(matcherName, undefined, undefined, options),
147
`${EXPECTED_COLOR('expected')} value must be a number`,
148
printWithType('Expected', expected, printExpected),
149
),
150
);
151
}
152
153
if (typeof received !== 'number') {
154
throw new Error(
155
matcherErrorMessage(
156
matcherHint(matcherName, undefined, undefined, options),
157
`${RECEIVED_COLOR('received')} value must be a number`,
158
printWithType('Received', received, printReceived),
159
),
160
);
161
}
164
let expectedDiff = 0;
165
let receivedDiff = 0;
166
167
if (received === Infinity && expected === Infinity) {
168
pass = true; // Infinity - Infinity is NaN
169
} else if (received === -Infinity && expected === -Infinity) {
170
pass = true; // -Infinity - -Infinity is NaN
171
} else {
172
expectedDiff = Math.pow(10, -precision) / 2;
173
receivedDiff = Math.abs(expected - received);
174
pass = receivedDiff < expectedDiff;
175
}
177
const message = pass
178
? () =>
179
// eslint-disable-next-line prefer-template
180
matcherHint(matcherName, undefined, undefined, options) +
181
'\n\n' +
182
`Expected: not ${printExpected(expected)}\n` +
183
(receivedDiff === 0
184
? ''
185
: `Received: ${printReceived(received)}\n` +
186
`\n${printCloseTo(receivedDiff, expectedDiff, precision, isNot)}`)
188
// eslint-disable-next-line prefer-template
189
matcherHint(matcherName, undefined, undefined, options) +
190
'\n\n' +
191
`Expected: ${printExpected(expected)}\n` +
192
`Received: ${printReceived(received)}\n` +
193
'\n' +
194
printCloseTo(receivedDiff, expectedDiff, precision, isNot);
195
196
return {message, pass};
197
},
199
toBeDefined(received: unknown, expected: void) {
200
const matcherName = 'toBeDefined';
201
const options: MatcherHintOptions = {
202
isNot: this.isNot,
203
promise: this.promise,
204
};
205
ensureNoExpected(expected, matcherName, options);
206
207
const pass = received !== void 0;
208
210
// eslint-disable-next-line prefer-template
211
matcherHint(matcherName, undefined, '', options) +
213
`Received: ${printReceived(received)}`;
214
215
return {message, pass};
216
},
217
218
toBeFalsy(received: unknown, expected: void) {
219
const matcherName = 'toBeFalsy';
220
const options: MatcherHintOptions = {
221
isNot: this.isNot,
222
promise: this.promise,
223
};
224
ensureNoExpected(expected, matcherName, options);
229
// eslint-disable-next-line prefer-template
230
matcherHint(matcherName, undefined, '', options) +
232
`Received: ${printReceived(received)}`;
233
234
return {message, pass};
235
},
236
237
toBeGreaterThan(received: number | bigint, expected: number | bigint) {
238
const matcherName = 'toBeGreaterThan';
239
const isNot = this.isNot;
240
const options: MatcherHintOptions = {
241
isNot,
242
promise: this.promise,
243
};
244
ensureNumbers(received, expected, matcherName, options);
245
246
const pass = received > expected;
247
249
// eslint-disable-next-line prefer-template
250
matcherHint(matcherName, undefined, undefined, options) +
252
`Expected:${isNot ? ' not' : ''} > ${printExpected(expected)}\n` +
253
`Received:${isNot ? ' ' : ''} ${printReceived(received)}`;
254
255
return {message, pass};
256
},
258
toBeGreaterThanOrEqual(received: number | bigint, expected: number | bigint) {
259
const matcherName = 'toBeGreaterThanOrEqual';
260
const isNot = this.isNot;
261
const options: MatcherHintOptions = {
262
isNot,
263
promise: this.promise,
264
};
265
ensureNumbers(received, expected, matcherName, options);
266
267
const pass = received >= expected;
268
270
// eslint-disable-next-line prefer-template
271
matcherHint(matcherName, undefined, undefined, options) +
273
`Expected:${isNot ? ' not' : ''} >= ${printExpected(expected)}\n` +
274
`Received:${isNot ? ' ' : ''} ${printReceived(received)}`;
275
276
return {message, pass};
277
},
278
279
toBeInstanceOf(received: any, expected: Function) {
280
const matcherName = 'toBeInstanceOf';
281
const options: MatcherHintOptions = {
282
isNot: this.isNot,
283
promise: this.promise,
284
};
286
if (typeof expected !== 'function') {
287
throw new Error(
289
matcherHint(matcherName, undefined, undefined, options),
290
`${EXPECTED_COLOR('expected')} value must be a function`,
291
printWithType('Expected', expected, printExpected),
295
296
const pass = received instanceof expected;
297
298
const message = pass
299
? () =>
300
// eslint-disable-next-line prefer-template
301
matcherHint(matcherName, undefined, undefined, options) +
302
'\n\n' +
303
printExpectedConstructorNameNot('Expected constructor', expected) +
304
(typeof received.constructor === 'function' &&
305
received.constructor !== expected
306
? printReceivedConstructorNameNot(
307
'Received constructor',
308
received.constructor,
309
expected,
310
)
311
: '')
312
: () =>
313
// eslint-disable-next-line prefer-template
314
matcherHint(matcherName, undefined, undefined, options) +
315
'\n\n' +
316
printExpectedConstructorName('Expected constructor', expected) +
317
(isPrimitive(received) || Object.getPrototypeOf(received) === null
318
? `\nReceived value has no prototype\nReceived value: ${printReceived(
319
received,
320
)}`
321
: typeof received.constructor !== 'function'
322
? `\nReceived value: ${printReceived(received)}`
323
: printReceivedConstructorName(
324
'Received constructor',
325
received.constructor,
326
));
327
328
return {message, pass};
329
},
330
331
toBeLessThan(received: number | bigint, expected: number | bigint) {
332
const matcherName = 'toBeLessThan';
333
const isNot = this.isNot;
334
const options: MatcherHintOptions = {
335
isNot,
336
promise: this.promise,
337
};
338
ensureNumbers(received, expected, matcherName, options);
339
340
const pass = received < expected;
341
343
// eslint-disable-next-line prefer-template
344
matcherHint(matcherName, undefined, undefined, options) +
346
`Expected:${isNot ? ' not' : ''} < ${printExpected(expected)}\n` +
347
`Received:${isNot ? ' ' : ''} ${printReceived(received)}`;
348
349
return {message, pass};
350
},
351
352
toBeLessThanOrEqual(received: number | bigint, expected: number | bigint) {
353
const matcherName = 'toBeLessThanOrEqual';
354
const isNot = this.isNot;
355
const options: MatcherHintOptions = {
356
isNot,
357
promise: this.promise,
358
};
359
ensureNumbers(received, expected, matcherName, options);
360
361
const pass = received <= expected;
362
364
// eslint-disable-next-line prefer-template
365
matcherHint(matcherName, undefined, undefined, options) +
367
`Expected:${isNot ? ' not' : ''} <= ${printExpected(expected)}\n` +
368
`Received:${isNot ? ' ' : ''} ${printReceived(received)}`;
369
370
return {message, pass};
371
},
372
373
toBeNaN(received: any, expected: void) {
375
const options: MatcherHintOptions = {
376
isNot: this.isNot,
377
promise: this.promise,
378
};
379
ensureNoExpected(expected, matcherName, options);
380
381
const pass = Number.isNaN(received);
382
384
// eslint-disable-next-line prefer-template
385
matcherHint(matcherName, undefined, '', options) +
387
`Received: ${printReceived(received)}`;
388
389
return {message, pass};
390
},
392
toBeNull(received: unknown, expected: void) {
394
const options: MatcherHintOptions = {
395
isNot: this.isNot,
396
promise: this.promise,
397
};
398
ensureNoExpected(expected, matcherName, options);
399
400
const pass = received === null;
401
403
// eslint-disable-next-line prefer-template
404
matcherHint(matcherName, undefined, '', options) +
406
`Received: ${printReceived(received)}`;
407
408
return {message, pass};
409
},
410
411
toBeTruthy(received: unknown, expected: void) {
412
const matcherName = 'toBeTruthy';
413
const options: MatcherHintOptions = {
414
isNot: this.isNot,
415
promise: this.promise,
416
};
417
ensureNoExpected(expected, matcherName, options);
418
419
const pass = !!received;
420
422
// eslint-disable-next-line prefer-template
423
matcherHint(matcherName, undefined, '', options) +
425
`Received: ${printReceived(received)}`;
426
427
return {message, pass};
428
},
429
430
toBeUndefined(received: unknown, expected: void) {
431
const matcherName = 'toBeUndefined';
432
const options: MatcherHintOptions = {
433
isNot: this.isNot,
434
promise: this.promise,
435
};
436
ensureNoExpected(expected, matcherName, options);
437
438
const pass = received === void 0;
439
441
// eslint-disable-next-line prefer-template
442
matcherHint(matcherName, undefined, '', options) +
444
`Received: ${printReceived(received)}`;
445
446
return {message, pass};
447
},
448
449
toContain(received: ContainIterable | string, expected: unknown) {
450
const matcherName = 'toContain';
451
const isNot = this.isNot;
452
const options: MatcherHintOptions = {
453
comment: 'indexOf',
454
isNot,
455
promise: this.promise,
456
};
457
458
if (received == null) {
459
throw new Error(
460
matcherErrorMessage(
461
matcherHint(matcherName, undefined, undefined, options),
462
`${RECEIVED_COLOR('received')} value must not be null nor undefined`,
463
printWithType('Received', received, printReceived),
464
),
465
);
466
}
467
468
if (typeof received === 'string') {
469
const wrongTypeErrorMessage = `${EXPECTED_COLOR(
470
'expected',
471
)} value must be a string if ${RECEIVED_COLOR(
472
'received',
473
)} value is a string`;
474
475
if (typeof expected !== 'string') {
476
throw new Error(
477
matcherErrorMessage(
478
matcherHint(matcherName, received, String(expected), options),
479
wrongTypeErrorMessage,
480
// eslint-disable-next-line prefer-template
481
printWithType('Expected', expected, printExpected) +
482
'\n' +
483
printWithType('Received', received, printReceived),
484
),
485
);
486
}
487
488
const index = received.indexOf(String(expected));
489
const pass = index !== -1;
490
491
const message = () => {
492
const labelExpected = `Expected ${
493
typeof expected === 'string' ? 'substring' : 'value'
494
}`;
495
const labelReceived = 'Received string';
496
const printLabel = getLabelPrinter(labelExpected, labelReceived);
497
498
return (
499
// eslint-disable-next-line prefer-template
500
matcherHint(matcherName, undefined, undefined, options) +
501
'\n\n' +
502
`${printLabel(labelExpected)}${isNot ? 'not ' : ''}${printExpected(
503
expected,
504
)}\n` +
505
`${printLabel(labelReceived)}${isNot ? ' ' : ''}${
506
isNot
507
? printReceivedStringContainExpectedSubstring(
508
received,
509
index,
510
String(expected).length,
511
)
512
: printReceived(received)
513
}`
515
};
516
517
return {message, pass};
519
520
const indexable = Array.from(received);
521
const index = indexable.indexOf(expected);
522
const pass = index !== -1;
523
524
const message = () => {
525
const labelExpected = 'Expected value';
526
const labelReceived = `Received ${getType(received)}`;
527
const printLabel = getLabelPrinter(labelExpected, labelReceived);
530
// eslint-disable-next-line prefer-template
531
matcherHint(matcherName, undefined, undefined, options) +
533
`${printLabel(labelExpected)}${isNot ? 'not ' : ''}${printExpected(
534
expected,
535
)}\n` +
536
`${printLabel(labelReceived)}${isNot ? ' ' : ''}${
537
isNot && Array.isArray(received)
538
? printReceivedArrayContainExpectedItem(received, index)
539
: printReceived(received)
540
}` +
541
(!isNot &&
542
indexable.findIndex(item =>
543
equals(item, expected, [iterableEquality]),
544
) !== -1
545
? `\n\n${SUGGEST_TO_CONTAIN_EQUAL}`
546
: '')
549
550
return {message, pass};
551
},
553
toContainEqual(received: ContainIterable, expected: unknown) {
554
const matcherName = 'toContainEqual';
555
const isNot = this.isNot;
556
const options: MatcherHintOptions = {
557
comment: 'deep equality',
558
isNot,
559
promise: this.promise,
560
};
561
562
if (received == null) {
563
throw new Error(
564
matcherErrorMessage(
565
matcherHint(matcherName, undefined, undefined, options),
566
`${RECEIVED_COLOR('received')} value must not be null nor undefined`,
567
printWithType('Received', received, printReceived),
568
),
569
);
572
const index = Array.from(received).findIndex(item =>
573
equals(item, expected, [iterableEquality]),
574
);
575
const pass = index !== -1;
576
577
const message = () => {
578
const labelExpected = 'Expected value';
579
const labelReceived = `Received ${getType(received)}`;
580
const printLabel = getLabelPrinter(labelExpected, labelReceived);
583
// eslint-disable-next-line prefer-template
584
matcherHint(matcherName, undefined, undefined, options) +
586
`${printLabel(labelExpected)}${isNot ? 'not ' : ''}${printExpected(
587
expected,
588
)}\n` +
589
`${printLabel(labelReceived)}${isNot ? ' ' : ''}${
590
isNot && Array.isArray(received)
591
? printReceivedArrayContainExpectedItem(received, index)
592
: printReceived(received)
593
}`
596
597
return {message, pass};
598
},
599
600
toEqual(received: unknown, expected: unknown) {
601
const matcherName = 'toEqual';
602
const options: MatcherHintOptions = {
603
comment: 'deep equality',
605
promise: this.promise,
608
const pass = equals(received, expected, [iterableEquality]);
609
610
const message = pass
611
? () =>
612
// eslint-disable-next-line prefer-template
613
matcherHint(matcherName, undefined, undefined, options) +
614
'\n\n' +
615
`Expected: not ${printExpected(expected)}\n` +
616
(stringify(expected) !== stringify(received)
617
? `Received: ${printReceived(received)}`
618
: '')
619
: () =>
620
// eslint-disable-next-line prefer-template
621
matcherHint(matcherName, undefined, undefined, options) +
622
'\n\n' +
623
printDiffOrStringify(
624
expected,
625
received,
626
EXPECTED_LABEL,
627
RECEIVED_LABEL,
631
// Passing the actual and expected objects so that a custom reporter
632
// could access them, for example in order to display a custom visual diff,
633
// or create a different error message
634
return {actual: received, expected, message, name: matcherName, pass};
637
toHaveLength(received: any, expected: number) {
638
const matcherName = 'toHaveLength';
639
const isNot = this.isNot;
640
const options: MatcherHintOptions = {
641
isNot,
642
promise: this.promise,
643
};
644
645
if (typeof received?.length !== 'number') {
646
throw new Error(
648
matcherHint(matcherName, undefined, undefined, options),
649
`${RECEIVED_COLOR(
650
'received',
651
)} value must have a length property whose value must be a number`,
652
printWithType('Received', received, printReceived),
653
),
654
);
655
}
656
657
ensureExpectedIsNonNegativeInteger(expected, matcherName, options);
658
659
const pass = received.length === expected;
660
661
const message = () => {
662
const labelExpected = 'Expected length';
663
const labelReceivedLength = 'Received length';
664
const labelReceivedValue = `Received ${getType(received)}`;
665
const printLabel = getLabelPrinter(
666
labelExpected,
667
labelReceivedLength,
668
labelReceivedValue,
672
// eslint-disable-next-line prefer-template
673
matcherHint(matcherName, undefined, undefined, options) +
675
`${printLabel(labelExpected)}${isNot ? 'not ' : ''}${printExpected(
676
expected,
678
(isNot
679
? ''
680
: `${printLabel(labelReceivedLength)}${printReceived(
681
received.length,
682
)}\n`) +
683
`${printLabel(labelReceivedValue)}${isNot ? ' ' : ''}${printReceived(
684
received,
685
)}`
688
689
return {message, pass};
690
},
691
692
toHaveProperty(
693
received: object,
694
expectedPath: string | Array<string>,
695
expectedValue?: unknown,
697
const matcherName = 'toHaveProperty';
698
const expectedArgument = 'path';
699
const hasValue = arguments.length === 3;
700
const options: MatcherHintOptions = {
701
isNot: this.isNot,
702
promise: this.promise,
703
secondArgument: hasValue ? 'value' : '',
706
if (received === null || received === undefined) {
707
throw new Error(
709
matcherHint(matcherName, undefined, expectedArgument, options),
710
`${RECEIVED_COLOR('received')} value must not be null nor undefined`,
711
printWithType('Received', received, printReceived),
713
);
714
}
715
716
const expectedPathType = getType(expectedPath);
718
if (expectedPathType !== 'string' && expectedPathType !== 'array') {
719
throw new Error(
721
matcherHint(matcherName, undefined, expectedArgument, options),
722
`${EXPECTED_COLOR('expected')} path must be a string or array`,
723
printWithType('Expected', expectedPath, printExpected),
725
);
726
}
727
728
const expectedPathLength =
729
typeof expectedPath === 'string'
730
? pathAsArray(expectedPath).length
731
: expectedPath.length;
732
733
if (expectedPathType === 'array' && expectedPathLength === 0) {
734
throw new Error(
735
matcherErrorMessage(
736
matcherHint(matcherName, undefined, expectedArgument, options),
737
`${EXPECTED_COLOR('expected')} path must not be an empty array`,
738
printWithType('Expected', expectedPath, printExpected),
739
),
740
);
741
}
743
const result = getPath(received, expectedPath);
744
const {lastTraversedObject, endPropIsDefined, hasEndProp, value} = result;
745
const receivedPath = result.traversedPath;
746
const hasCompletePath = receivedPath.length === expectedPathLength;
747
const receivedValue = hasCompletePath ? result.value : lastTraversedObject;
748
749
const pass =
750
hasValue && endPropIsDefined
751
? equals(value, expectedValue, [iterableEquality])
752
: Boolean(hasEndProp);
753
754
const message = pass
756
// eslint-disable-next-line prefer-template
757
matcherHint(matcherName, undefined, expectedArgument, options) +
759
(hasValue
760
? `Expected path: ${printExpected(expectedPath)}\n\n` +
761
`Expected value: not ${printExpected(expectedValue)}${
762
stringify(expectedValue) !== stringify(receivedValue)
763
? `\nReceived value: ${printReceived(receivedValue)}`
764
: ''
765
}`
766
: `Expected path: not ${printExpected(expectedPath)}\n\n` +
767
`Received value: ${printReceived(receivedValue)}`)
768
: () =>
769
// eslint-disable-next-line prefer-template
770
matcherHint(matcherName, undefined, expectedArgument, options) +
771
'\n\n' +
772
`Expected path: ${printExpected(expectedPath)}\n` +
773
(hasCompletePath
774
? `\n${printDiffOrStringify(
775
expectedValue,
776
receivedValue,
777
EXPECTED_VALUE_LABEL,
778
RECEIVED_VALUE_LABEL,
781
: `Received path: ${printReceived(
782
expectedPathType === 'array' || receivedPath.length === 0
783
? receivedPath
784
: receivedPath.join('.'),
785
)}\n\n${
786
hasValue
787
? `Expected value: ${printExpected(expectedValue)}\n`
788
: ''
789
}Received value: ${printReceived(receivedValue)}`);
790
791
return {message, pass};
792
},
793
794
toMatch(received: string, expected: string | RegExp) {
796
const options: MatcherHintOptions = {
797
isNot: this.isNot,
798
promise: this.promise,
799
};
800
801
if (typeof received !== 'string') {
802
throw new Error(
804
matcherHint(matcherName, undefined, undefined, options),
805
`${RECEIVED_COLOR('received')} value must be a string`,
806
printWithType('Received', received, printReceived),
808
);
812
!(typeof expected === 'string') &&
813
!(expected && typeof expected.test === 'function')
815
throw new Error(
817
matcherHint(matcherName, undefined, undefined, options),
818
`${EXPECTED_COLOR(
819
'expected',
820
)} value must be a string or regular expression`,
821
printWithType('Expected', expected, printExpected),
823
);
826
const pass =
827
typeof expected === 'string'
828
? received.includes(expected)
829
: new RegExp(expected).test(received);
831
const message = pass
832
? () =>
833
typeof expected === 'string'
834
? // eslint-disable-next-line prefer-template
835
matcherHint(matcherName, undefined, undefined, options) +
836
'\n\n' +
837
`Expected substring: not ${printExpected(expected)}\n` +
838
`Received string: ${printReceivedStringContainExpectedSubstring(
839
received,
840
received.indexOf(expected),
841
expected.length,
842
)}`
843
: // eslint-disable-next-line prefer-template
844
matcherHint(matcherName, undefined, undefined, options) +
845
'\n\n' +
846
`Expected pattern: not ${printExpected(expected)}\n` +
847
`Received string: ${printReceivedStringContainExpectedResult(
848
received,
849
typeof expected.exec === 'function'
850
? expected.exec(received)
851
: null,
852
)}`
853
: () => {
854
const labelExpected = `Expected ${
855
typeof expected === 'string' ? 'substring' : 'pattern'
856
}`;
857
const labelReceived = 'Received string';
858
const printLabel = getLabelPrinter(labelExpected, labelReceived);
859
860
return (
861
// eslint-disable-next-line prefer-template
862
matcherHint(matcherName, undefined, undefined, options) +
863
'\n\n' +
864
`${printLabel(labelExpected)}${printExpected(expected)}\n` +
865
`${printLabel(labelReceived)}${printReceived(received)}`
866
);
867
};
868
869
return {message, pass};
870
},
872
toMatchObject(received: object, expected: object) {
873
const matcherName = 'toMatchObject';
874
const options: MatcherHintOptions = {
875
isNot: this.isNot,
876
promise: this.promise,
879
if (typeof received !== 'object' || received === null) {
882
matcherHint(matcherName, undefined, undefined, options),
883
`${RECEIVED_COLOR('received')} value must be a non-null object`,
884
printWithType('Received', received, printReceived),
889
if (typeof expected !== 'object' || expected === null) {
892
matcherHint(matcherName, undefined, undefined, options),
893
`${EXPECTED_COLOR('expected')} value must be a non-null object`,
894
printWithType('Expected', expected, printExpected),
899
const pass = equals(received, expected, [iterableEquality, subsetEquality]);
900
901
const message = pass
902
? () =>
903
// eslint-disable-next-line prefer-template
904
matcherHint(matcherName, undefined, undefined, options) +
905
'\n\n' +
906
`Expected: not ${printExpected(expected)}` +
907
(stringify(expected) !== stringify(received)
908
? `\nReceived: ${printReceived(received)}`
909
: '')
910
: () =>
911
// eslint-disable-next-line prefer-template
912
matcherHint(matcherName, undefined, undefined, options) +
913
'\n\n' +
914
printDiffOrStringify(
915
expected,
916
getObjectSubset(received, expected),
917
EXPECTED_LABEL,
918
RECEIVED_LABEL,
921
922
return {message, pass};
923
},
925
toStrictEqual(received: unknown, expected: unknown) {
926
const matcherName = 'toStrictEqual';
927
const options: MatcherHintOptions = {
928
comment: 'deep equality',
930
promise: this.promise,
933
const pass = equals(received, expected, toStrictEqualTesters, true);
934
935
const message = pass
936
? () =>
937
// eslint-disable-next-line prefer-template
938
matcherHint(matcherName, undefined, undefined, options) +
939
'\n\n' +
940
`Expected: not ${printExpected(expected)}\n` +
941
(stringify(expected) !== stringify(received)
942
? `Received: ${printReceived(received)}`
943
: '')
944
: () =>
945
// eslint-disable-next-line prefer-template
946
matcherHint(matcherName, undefined, undefined, options) +
947
'\n\n' +
948
printDiffOrStringify(
949
expected,
950
received,
951
EXPECTED_LABEL,
952
RECEIVED_LABEL,
956
// Passing the actual and expected objects so that a custom reporter
957
// could access them, for example in order to display a custom visual diff,
958
// or create a different error message
959
return {actual: received, expected, message, name: matcherName, pass};
961
};
962
963
export default matchers;