1 | 'use strict';
|
2 |
|
3 | Object.defineProperty(exports, '__esModule', {
|
4 | value: true
|
5 | });
|
6 | exports.matcherHint =
|
7 | exports.matcherErrorMessage =
|
8 | exports.getLabelPrinter =
|
9 | exports.pluralize =
|
10 | exports.diff =
|
11 | exports.printDiffOrStringify =
|
12 | exports.ensureExpectedIsNonNegativeInteger =
|
13 | exports.ensureNumbers =
|
14 | exports.ensureExpectedIsNumber =
|
15 | exports.ensureActualIsNumber =
|
16 | exports.ensureNoExpected =
|
17 | exports.printWithType =
|
18 | exports.printExpected =
|
19 | exports.printReceived =
|
20 | exports.highlightTrailingWhitespace =
|
21 | exports.stringify =
|
22 | exports.SUGGEST_TO_CONTAIN_EQUAL =
|
23 | exports.DIM_COLOR =
|
24 | exports.BOLD_WEIGHT =
|
25 | exports.INVERTED_COLOR =
|
26 | exports.RECEIVED_COLOR =
|
27 | exports.EXPECTED_COLOR =
|
28 | void 0;
|
29 |
|
30 | var _chalk = _interopRequireDefault(require('chalk'));
|
31 |
|
32 | var _jestDiff = require('jest-diff');
|
33 |
|
34 | var _jestGetType = require('jest-get-type');
|
35 |
|
36 | var _prettyFormat = require('pretty-format');
|
37 |
|
38 | var _Replaceable = _interopRequireDefault(require('./Replaceable'));
|
39 |
|
40 | var _deepCyclicCopyReplaceable = _interopRequireDefault(
|
41 | require('./deepCyclicCopyReplaceable')
|
42 | );
|
43 |
|
44 | function _interopRequireDefault(obj) {
|
45 | return obj && obj.__esModule ? obj : {default: obj};
|
46 | }
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 | const {
|
57 | AsymmetricMatcher,
|
58 | DOMCollection,
|
59 | DOMElement,
|
60 | Immutable,
|
61 | ReactElement,
|
62 | ReactTestComponent
|
63 | } = _prettyFormat.plugins;
|
64 | const PLUGINS = [
|
65 | ReactTestComponent,
|
66 | ReactElement,
|
67 | DOMElement,
|
68 | DOMCollection,
|
69 | Immutable,
|
70 | AsymmetricMatcher
|
71 | ];
|
72 | const EXPECTED_COLOR = _chalk.default.green;
|
73 | exports.EXPECTED_COLOR = EXPECTED_COLOR;
|
74 | const RECEIVED_COLOR = _chalk.default.red;
|
75 | exports.RECEIVED_COLOR = RECEIVED_COLOR;
|
76 | const INVERTED_COLOR = _chalk.default.inverse;
|
77 | exports.INVERTED_COLOR = INVERTED_COLOR;
|
78 | const BOLD_WEIGHT = _chalk.default.bold;
|
79 | exports.BOLD_WEIGHT = BOLD_WEIGHT;
|
80 | const DIM_COLOR = _chalk.default.dim;
|
81 | exports.DIM_COLOR = DIM_COLOR;
|
82 | const MULTILINE_REGEXP = /\n/;
|
83 | const SPACE_SYMBOL = '\u{00B7}';
|
84 |
|
85 | const NUMBERS = [
|
86 | 'zero',
|
87 | 'one',
|
88 | 'two',
|
89 | 'three',
|
90 | 'four',
|
91 | 'five',
|
92 | 'six',
|
93 | 'seven',
|
94 | 'eight',
|
95 | 'nine',
|
96 | 'ten',
|
97 | 'eleven',
|
98 | 'twelve',
|
99 | 'thirteen'
|
100 | ];
|
101 |
|
102 | const SUGGEST_TO_CONTAIN_EQUAL = _chalk.default.dim(
|
103 | 'Looks like you wanted to test for object/array equality with the stricter `toContain` matcher. You probably need to use `toContainEqual` instead.'
|
104 | );
|
105 |
|
106 | exports.SUGGEST_TO_CONTAIN_EQUAL = SUGGEST_TO_CONTAIN_EQUAL;
|
107 |
|
108 | const stringify = (object, maxDepth = 10) => {
|
109 | const MAX_LENGTH = 10000;
|
110 | let result;
|
111 |
|
112 | try {
|
113 | result = (0, _prettyFormat.format)(object, {
|
114 | maxDepth,
|
115 | min: true,
|
116 | plugins: PLUGINS
|
117 | });
|
118 | } catch {
|
119 | result = (0, _prettyFormat.format)(object, {
|
120 | callToJSON: false,
|
121 | maxDepth,
|
122 | min: true,
|
123 | plugins: PLUGINS
|
124 | });
|
125 | }
|
126 |
|
127 | return result.length >= MAX_LENGTH && maxDepth > 1
|
128 | ? stringify(object, Math.floor(maxDepth / 2))
|
129 | : result;
|
130 | };
|
131 |
|
132 | exports.stringify = stringify;
|
133 |
|
134 | const highlightTrailingWhitespace = text =>
|
135 | text.replace(/\s+$/gm, _chalk.default.inverse('$&'));
|
136 |
|
137 |
|
138 | exports.highlightTrailingWhitespace = highlightTrailingWhitespace;
|
139 |
|
140 | const replaceTrailingSpaces = text =>
|
141 | text.replace(/\s+$/gm, spaces => SPACE_SYMBOL.repeat(spaces.length));
|
142 |
|
143 | const printReceived = object =>
|
144 | RECEIVED_COLOR(replaceTrailingSpaces(stringify(object)));
|
145 |
|
146 | exports.printReceived = printReceived;
|
147 |
|
148 | const printExpected = value =>
|
149 | EXPECTED_COLOR(replaceTrailingSpaces(stringify(value)));
|
150 |
|
151 | exports.printExpected = printExpected;
|
152 |
|
153 | const printWithType = (
|
154 | name,
|
155 | value,
|
156 | print
|
157 | ) => {
|
158 | const type = (0, _jestGetType.getType)(value);
|
159 | const hasType =
|
160 | type !== 'null' && type !== 'undefined'
|
161 | ? `${name} has type: ${type}\n`
|
162 | : '';
|
163 | const hasValue = `${name} has value: ${print(value)}`;
|
164 | return hasType + hasValue;
|
165 | };
|
166 |
|
167 | exports.printWithType = printWithType;
|
168 |
|
169 | const ensureNoExpected = (expected, matcherName, options) => {
|
170 | if (typeof expected !== 'undefined') {
|
171 |
|
172 | const matcherString = (options ? '' : '[.not]') + matcherName;
|
173 | throw new Error(
|
174 | matcherErrorMessage(
|
175 | matcherHint(matcherString, undefined, '', options),
|
176 |
|
177 | 'this matcher must not have an expected argument',
|
178 | printWithType('Expected', expected, printExpected)
|
179 | )
|
180 | );
|
181 | }
|
182 | };
|
183 |
|
184 |
|
185 |
|
186 |
|
187 | exports.ensureNoExpected = ensureNoExpected;
|
188 |
|
189 | const ensureActualIsNumber = (actual, matcherName, options) => {
|
190 | if (typeof actual !== 'number' && typeof actual !== 'bigint') {
|
191 |
|
192 | const matcherString = (options ? '' : '[.not]') + matcherName;
|
193 | throw new Error(
|
194 | matcherErrorMessage(
|
195 | matcherHint(matcherString, undefined, undefined, options),
|
196 | `${RECEIVED_COLOR('received')} value must be a number or bigint`,
|
197 | printWithType('Received', actual, printReceived)
|
198 | )
|
199 | );
|
200 | }
|
201 | };
|
202 |
|
203 |
|
204 |
|
205 |
|
206 | exports.ensureActualIsNumber = ensureActualIsNumber;
|
207 |
|
208 | const ensureExpectedIsNumber = (expected, matcherName, options) => {
|
209 | if (typeof expected !== 'number' && typeof expected !== 'bigint') {
|
210 |
|
211 | const matcherString = (options ? '' : '[.not]') + matcherName;
|
212 | throw new Error(
|
213 | matcherErrorMessage(
|
214 | matcherHint(matcherString, undefined, undefined, options),
|
215 | `${EXPECTED_COLOR('expected')} value must be a number or bigint`,
|
216 | printWithType('Expected', expected, printExpected)
|
217 | )
|
218 | );
|
219 | }
|
220 | };
|
221 |
|
222 |
|
223 |
|
224 |
|
225 | exports.ensureExpectedIsNumber = ensureExpectedIsNumber;
|
226 |
|
227 | const ensureNumbers = (actual, expected, matcherName, options) => {
|
228 | ensureActualIsNumber(actual, matcherName, options);
|
229 | ensureExpectedIsNumber(expected, matcherName, options);
|
230 | };
|
231 |
|
232 | exports.ensureNumbers = ensureNumbers;
|
233 |
|
234 | const ensureExpectedIsNonNegativeInteger = (expected, matcherName, options) => {
|
235 | if (
|
236 | typeof expected !== 'number' ||
|
237 | !Number.isSafeInteger(expected) ||
|
238 | expected < 0
|
239 | ) {
|
240 |
|
241 | const matcherString = (options ? '' : '[.not]') + matcherName;
|
242 | throw new Error(
|
243 | matcherErrorMessage(
|
244 | matcherHint(matcherString, undefined, undefined, options),
|
245 | `${EXPECTED_COLOR('expected')} value must be a non-negative integer`,
|
246 | printWithType('Expected', expected, printExpected)
|
247 | )
|
248 | );
|
249 | }
|
250 | };
|
251 |
|
252 |
|
253 |
|
254 |
|
255 |
|
256 | exports.ensureExpectedIsNonNegativeInteger = ensureExpectedIsNonNegativeInteger;
|
257 |
|
258 | const getCommonAndChangedSubstrings = (diffs, op, hasCommonDiff) =>
|
259 | diffs.reduce(
|
260 | (reduced, diff) =>
|
261 | reduced +
|
262 | (diff[0] === _jestDiff.DIFF_EQUAL
|
263 | ? diff[1]
|
264 | : diff[0] !== op
|
265 | ? ''
|
266 | : hasCommonDiff
|
267 | ? INVERTED_COLOR(diff[1])
|
268 | : diff[1]),
|
269 | ''
|
270 | );
|
271 |
|
272 | const isLineDiffable = (expected, received) => {
|
273 | const expectedType = (0, _jestGetType.getType)(expected);
|
274 | const receivedType = (0, _jestGetType.getType)(received);
|
275 |
|
276 | if (expectedType !== receivedType) {
|
277 | return false;
|
278 | }
|
279 |
|
280 | if ((0, _jestGetType.isPrimitive)(expected)) {
|
281 |
|
282 |
|
283 |
|
284 | return (
|
285 | typeof expected === 'string' &&
|
286 | typeof received === 'string' &&
|
287 | expected.length !== 0 &&
|
288 | received.length !== 0 &&
|
289 | (MULTILINE_REGEXP.test(expected) || MULTILINE_REGEXP.test(received))
|
290 | );
|
291 | }
|
292 |
|
293 | if (
|
294 | expectedType === 'date' ||
|
295 | expectedType === 'function' ||
|
296 | expectedType === 'regexp'
|
297 | ) {
|
298 | return false;
|
299 | }
|
300 |
|
301 | if (expected instanceof Error && received instanceof Error) {
|
302 | return false;
|
303 | }
|
304 |
|
305 | if (
|
306 | expectedType === 'object' &&
|
307 | typeof expected.asymmetricMatch === 'function'
|
308 | ) {
|
309 | return false;
|
310 | }
|
311 |
|
312 | if (
|
313 | receivedType === 'object' &&
|
314 | typeof received.asymmetricMatch === 'function'
|
315 | ) {
|
316 | return false;
|
317 | }
|
318 |
|
319 | return true;
|
320 | };
|
321 |
|
322 | const MAX_DIFF_STRING_LENGTH = 20000;
|
323 |
|
324 | const printDiffOrStringify = (
|
325 | expected,
|
326 | received,
|
327 | expectedLabel,
|
328 | receivedLabel,
|
329 | expand
|
330 | ) => {
|
331 | if (
|
332 | typeof expected === 'string' &&
|
333 | typeof received === 'string' &&
|
334 | expected.length !== 0 &&
|
335 | received.length !== 0 &&
|
336 | expected.length <= MAX_DIFF_STRING_LENGTH &&
|
337 | received.length <= MAX_DIFF_STRING_LENGTH &&
|
338 | expected !== received
|
339 | ) {
|
340 | if (expected.includes('\n') || received.includes('\n')) {
|
341 | return (0, _jestDiff.diffStringsUnified)(expected, received, {
|
342 | aAnnotation: expectedLabel,
|
343 | bAnnotation: receivedLabel,
|
344 | changeLineTrailingSpaceColor: _chalk.default.bgYellow,
|
345 | commonLineTrailingSpaceColor: _chalk.default.bgYellow,
|
346 | emptyFirstOrLastLinePlaceholder: '↵',
|
347 |
|
348 | expand,
|
349 | includeChangeCounts: true
|
350 | });
|
351 | }
|
352 |
|
353 | const diffs = (0, _jestDiff.diffStringsRaw)(expected, received, true);
|
354 | const hasCommonDiff = diffs.some(diff => diff[0] === _jestDiff.DIFF_EQUAL);
|
355 | const printLabel = getLabelPrinter(expectedLabel, receivedLabel);
|
356 | const expectedLine =
|
357 | printLabel(expectedLabel) +
|
358 | printExpected(
|
359 | getCommonAndChangedSubstrings(
|
360 | diffs,
|
361 | _jestDiff.DIFF_DELETE,
|
362 | hasCommonDiff
|
363 | )
|
364 | );
|
365 | const receivedLine =
|
366 | printLabel(receivedLabel) +
|
367 | printReceived(
|
368 | getCommonAndChangedSubstrings(
|
369 | diffs,
|
370 | _jestDiff.DIFF_INSERT,
|
371 | hasCommonDiff
|
372 | )
|
373 | );
|
374 | return expectedLine + '\n' + receivedLine;
|
375 | }
|
376 |
|
377 | if (isLineDiffable(expected, received)) {
|
378 | const {replacedExpected, replacedReceived} =
|
379 | replaceMatchedToAsymmetricMatcher(
|
380 | (0, _deepCyclicCopyReplaceable.default)(expected),
|
381 | (0, _deepCyclicCopyReplaceable.default)(received),
|
382 | [],
|
383 | []
|
384 | );
|
385 | const difference = (0, _jestDiff.diff)(replacedExpected, replacedReceived, {
|
386 | aAnnotation: expectedLabel,
|
387 | bAnnotation: receivedLabel,
|
388 | expand,
|
389 | includeChangeCounts: true
|
390 | });
|
391 |
|
392 | if (
|
393 | typeof difference === 'string' &&
|
394 | difference.includes('- ' + expectedLabel) &&
|
395 | difference.includes('+ ' + receivedLabel)
|
396 | ) {
|
397 | return difference;
|
398 | }
|
399 | }
|
400 |
|
401 | const printLabel = getLabelPrinter(expectedLabel, receivedLabel);
|
402 | const expectedLine = printLabel(expectedLabel) + printExpected(expected);
|
403 | const receivedLine =
|
404 | printLabel(receivedLabel) +
|
405 | (stringify(expected) === stringify(received)
|
406 | ? 'serializes to the same string'
|
407 | : printReceived(received));
|
408 | return expectedLine + '\n' + receivedLine;
|
409 | };
|
410 |
|
411 |
|
412 |
|
413 | exports.printDiffOrStringify = printDiffOrStringify;
|
414 |
|
415 | const shouldPrintDiff = (actual, expected) => {
|
416 | if (typeof actual === 'number' && typeof expected === 'number') {
|
417 | return false;
|
418 | }
|
419 |
|
420 | if (typeof actual === 'bigint' && typeof expected === 'bigint') {
|
421 | return false;
|
422 | }
|
423 |
|
424 | if (typeof actual === 'boolean' && typeof expected === 'boolean') {
|
425 | return false;
|
426 | }
|
427 |
|
428 | return true;
|
429 | };
|
430 |
|
431 | function replaceMatchedToAsymmetricMatcher(
|
432 | replacedExpected,
|
433 | replacedReceived,
|
434 | expectedCycles,
|
435 | receivedCycles
|
436 | ) {
|
437 | if (!_Replaceable.default.isReplaceable(replacedExpected, replacedReceived)) {
|
438 | return {
|
439 | replacedExpected,
|
440 | replacedReceived
|
441 | };
|
442 | }
|
443 |
|
444 | if (
|
445 | expectedCycles.includes(replacedExpected) ||
|
446 | receivedCycles.includes(replacedReceived)
|
447 | ) {
|
448 | return {
|
449 | replacedExpected,
|
450 | replacedReceived
|
451 | };
|
452 | }
|
453 |
|
454 | expectedCycles.push(replacedExpected);
|
455 | receivedCycles.push(replacedReceived);
|
456 | const expectedReplaceable = new _Replaceable.default(replacedExpected);
|
457 | const receivedReplaceable = new _Replaceable.default(replacedReceived);
|
458 | expectedReplaceable.forEach((expectedValue, key) => {
|
459 | const receivedValue = receivedReplaceable.get(key);
|
460 |
|
461 | if (isAsymmetricMatcher(expectedValue)) {
|
462 | if (expectedValue.asymmetricMatch(receivedValue)) {
|
463 | receivedReplaceable.set(key, expectedValue);
|
464 | }
|
465 | } else if (isAsymmetricMatcher(receivedValue)) {
|
466 | if (receivedValue.asymmetricMatch(expectedValue)) {
|
467 | expectedReplaceable.set(key, receivedValue);
|
468 | }
|
469 | } else if (
|
470 | _Replaceable.default.isReplaceable(expectedValue, receivedValue)
|
471 | ) {
|
472 | const replaced = replaceMatchedToAsymmetricMatcher(
|
473 | expectedValue,
|
474 | receivedValue,
|
475 | expectedCycles,
|
476 | receivedCycles
|
477 | );
|
478 | expectedReplaceable.set(key, replaced.replacedExpected);
|
479 | receivedReplaceable.set(key, replaced.replacedReceived);
|
480 | }
|
481 | });
|
482 | return {
|
483 | replacedExpected: expectedReplaceable.object,
|
484 | replacedReceived: receivedReplaceable.object
|
485 | };
|
486 | }
|
487 |
|
488 | function isAsymmetricMatcher(data) {
|
489 | const type = (0, _jestGetType.getType)(data);
|
490 | return type === 'object' && typeof data.asymmetricMatch === 'function';
|
491 | }
|
492 |
|
493 | const diff = (a, b, options) =>
|
494 | shouldPrintDiff(a, b) ? (0, _jestDiff.diff)(a, b, options) : null;
|
495 |
|
496 | exports.diff = diff;
|
497 |
|
498 | const pluralize = (word, count) =>
|
499 | (NUMBERS[count] || count) + ' ' + word + (count === 1 ? '' : 's');
|
500 |
|
501 |
|
502 |
|
503 |
|
504 | exports.pluralize = pluralize;
|
505 |
|
506 | const getLabelPrinter = (...strings) => {
|
507 | const maxLength = strings.reduce(
|
508 | (max, string) => (string.length > max ? string.length : max),
|
509 | 0
|
510 | );
|
511 | return string => `${string}: ${' '.repeat(maxLength - string.length)}`;
|
512 | };
|
513 |
|
514 | exports.getLabelPrinter = getLabelPrinter;
|
515 |
|
516 | const matcherErrorMessage = (
|
517 | hint,
|
518 | generic,
|
519 | specific
|
520 | ) =>
|
521 | `${hint}\n\n${_chalk.default.bold('Matcher error')}: ${generic}${
|
522 | typeof specific === 'string' ? '\n\n' + specific : ''
|
523 | }`;
|
524 |
|
525 |
|
526 |
|
527 | exports.matcherErrorMessage = matcherErrorMessage;
|
528 |
|
529 | const matcherHint = (
|
530 | matcherName,
|
531 | received = 'received',
|
532 | expected = 'expected',
|
533 | options = {}
|
534 | ) => {
|
535 | const {
|
536 | comment = '',
|
537 | expectedColor = EXPECTED_COLOR,
|
538 | isDirectExpectCall = false,
|
539 |
|
540 | isNot = false,
|
541 | promise = '',
|
542 | receivedColor = RECEIVED_COLOR,
|
543 | secondArgument = '',
|
544 | secondArgumentColor = EXPECTED_COLOR
|
545 | } = options;
|
546 | let hint = '';
|
547 | let dimString = 'expect';
|
548 |
|
549 | if (!isDirectExpectCall && received !== '') {
|
550 | hint += DIM_COLOR(dimString + '(') + receivedColor(received);
|
551 | dimString = ')';
|
552 | }
|
553 |
|
554 | if (promise !== '') {
|
555 | hint += DIM_COLOR(dimString + '.') + promise;
|
556 | dimString = '';
|
557 | }
|
558 |
|
559 | if (isNot) {
|
560 | hint += DIM_COLOR(dimString + '.') + 'not';
|
561 | dimString = '';
|
562 | }
|
563 |
|
564 | if (matcherName.includes('.')) {
|
565 |
|
566 |
|
567 | dimString += matcherName;
|
568 | } else {
|
569 |
|
570 | hint += DIM_COLOR(dimString + '.') + matcherName;
|
571 | dimString = '';
|
572 | }
|
573 |
|
574 | if (expected === '') {
|
575 | dimString += '()';
|
576 | } else {
|
577 | hint += DIM_COLOR(dimString + '(') + expectedColor(expected);
|
578 |
|
579 | if (secondArgument) {
|
580 | hint += DIM_COLOR(', ') + secondArgumentColor(secondArgument);
|
581 | }
|
582 |
|
583 | dimString = ')';
|
584 | }
|
585 |
|
586 | if (comment !== '') {
|
587 | dimString += ' // ' + comment;
|
588 | }
|
589 |
|
590 | if (dimString !== '') {
|
591 | hint += DIM_COLOR(dimString);
|
592 | }
|
593 |
|
594 | return hint;
|
595 | };
|
596 |
|
597 | exports.matcherHint = matcherHint;
|