You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

385 lines
10 KiB

2 months ago
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
exports.printSnapshotAndReceived = exports.printPropertiesAndReceived = exports.printReceived = exports.printExpected = exports.matcherHintFromConfig = exports.PROPERTIES_ARG = exports.SNAPSHOT_ARG = exports.HINT_ARG = exports.noColor = exports.bReceivedColor = exports.aSnapshotColor = exports.getReceivedColorForChalkInstance = exports.getSnapshotColorForChalkInstance = void 0;
var _chalk = _interopRequireDefault(require('chalk'));
var _utils = require('expect/build/utils');
var _jestDiff = require('jest-diff');
var _jestGetType = _interopRequireDefault(require('jest-get-type'));
var _jestMatcherUtils = require('jest-matcher-utils');
var _prettyFormat = _interopRequireDefault(require('pretty-format'));
var _colors = require('./colors');
var _dedentLines = require('./dedentLines');
var _utils2 = require('./utils');
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {default: obj};
}
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/* eslint-disable local/ban-types-eventually */
// Temporary hack because getObjectSubset has known limitations,
// is not in the public interface of the expect package,
// and the long-term goal is to use a non-serialization diff.
const getSnapshotColorForChalkInstance = chalkInstance => {
const level = chalkInstance.level;
if (level === 3) {
return chalkInstance
.rgb(
_colors.aForeground3[0],
_colors.aForeground3[1],
_colors.aForeground3[2]
)
.bgRgb(
_colors.aBackground3[0],
_colors.aBackground3[1],
_colors.aBackground3[2]
);
}
if (level === 2) {
return chalkInstance
.ansi256(_colors.aForeground2)
.bgAnsi256(_colors.aBackground2);
}
return chalkInstance.magenta.bgYellowBright;
};
exports.getSnapshotColorForChalkInstance = getSnapshotColorForChalkInstance;
const getReceivedColorForChalkInstance = chalkInstance => {
const level = chalkInstance.level;
if (level === 3) {
return chalkInstance
.rgb(
_colors.bForeground3[0],
_colors.bForeground3[1],
_colors.bForeground3[2]
)
.bgRgb(
_colors.bBackground3[0],
_colors.bBackground3[1],
_colors.bBackground3[2]
);
}
if (level === 2) {
return chalkInstance
.ansi256(_colors.bForeground2)
.bgAnsi256(_colors.bBackground2);
}
return chalkInstance.cyan.bgWhiteBright; // also known as teal
};
exports.getReceivedColorForChalkInstance = getReceivedColorForChalkInstance;
const aSnapshotColor = getSnapshotColorForChalkInstance(_chalk.default);
exports.aSnapshotColor = aSnapshotColor;
const bReceivedColor = getReceivedColorForChalkInstance(_chalk.default);
exports.bReceivedColor = bReceivedColor;
const noColor = string => string;
exports.noColor = noColor;
const HINT_ARG = 'hint';
exports.HINT_ARG = HINT_ARG;
const SNAPSHOT_ARG = 'snapshot';
exports.SNAPSHOT_ARG = SNAPSHOT_ARG;
const PROPERTIES_ARG = 'properties';
exports.PROPERTIES_ARG = PROPERTIES_ARG;
const matcherHintFromConfig = (
{context: {isNot, promise}, hint, inlineSnapshot, matcherName, properties},
isUpdatable
) => {
const options = {
isNot,
promise
};
if (isUpdatable) {
options.receivedColor = bReceivedColor;
}
let expectedArgument = '';
if (typeof properties === 'object') {
expectedArgument = PROPERTIES_ARG;
if (isUpdatable) {
options.expectedColor = noColor;
}
if (typeof hint === 'string' && hint.length !== 0) {
options.secondArgument = HINT_ARG;
options.secondArgumentColor = _jestMatcherUtils.BOLD_WEIGHT;
} else if (typeof inlineSnapshot === 'string') {
options.secondArgument = SNAPSHOT_ARG;
if (isUpdatable) {
options.secondArgumentColor = aSnapshotColor;
} else {
options.secondArgumentColor = noColor;
}
}
} else {
if (typeof hint === 'string' && hint.length !== 0) {
expectedArgument = HINT_ARG;
options.expectedColor = _jestMatcherUtils.BOLD_WEIGHT;
} else if (typeof inlineSnapshot === 'string') {
expectedArgument = SNAPSHOT_ARG;
if (isUpdatable) {
options.expectedColor = aSnapshotColor;
}
}
}
return (0, _jestMatcherUtils.matcherHint)(
matcherName,
undefined,
expectedArgument,
options
);
}; // Given array of diffs, return string:
// * include common substrings
// * exclude change substrings which have opposite op
// * include change substrings which have argument op
// with change color only if there is a common substring
exports.matcherHintFromConfig = matcherHintFromConfig;
const joinDiffs = (diffs, op, hasCommon) =>
diffs.reduce(
(reduced, diff) =>
reduced +
(diff[0] === _jestDiff.DIFF_EQUAL
? diff[1]
: diff[0] !== op
? ''
: hasCommon
? (0, _jestMatcherUtils.INVERTED_COLOR)(diff[1])
: diff[1]),
''
);
const isLineDiffable = received => {
const receivedType = (0, _jestGetType.default)(received);
if (_jestGetType.default.isPrimitive(received)) {
return typeof received === 'string';
}
if (
receivedType === 'date' ||
receivedType === 'function' ||
receivedType === 'regexp'
) {
return false;
}
if (received instanceof Error) {
return false;
}
if (
receivedType === 'object' &&
typeof received.asymmetricMatch === 'function'
) {
return false;
}
return true;
};
const printExpected = val =>
(0, _jestMatcherUtils.EXPECTED_COLOR)((0, _utils2.minify)(val));
exports.printExpected = printExpected;
const printReceived = val =>
(0, _jestMatcherUtils.RECEIVED_COLOR)((0, _utils2.minify)(val));
exports.printReceived = printReceived;
const printPropertiesAndReceived = (properties, received, expand) => {
const aAnnotation = 'Expected properties';
const bAnnotation = 'Received value';
if (isLineDiffable(properties) && isLineDiffable(received)) {
return (0, _jestDiff.diffLinesUnified)(
(0, _utils2.serialize)(properties).split('\n'),
(0, _utils2.serialize)(
(0, _utils.getObjectSubset)(received, properties)
).split('\n'),
{
aAnnotation,
aColor: _jestMatcherUtils.EXPECTED_COLOR,
bAnnotation,
bColor: _jestMatcherUtils.RECEIVED_COLOR,
changeLineTrailingSpaceColor: _chalk.default.bgYellow,
commonLineTrailingSpaceColor: _chalk.default.bgYellow,
emptyFirstOrLastLinePlaceholder: '↵',
// U+21B5
expand,
includeChangeCounts: true
}
);
}
const printLabel = (0, _jestMatcherUtils.getLabelPrinter)(
aAnnotation,
bAnnotation
);
return (
printLabel(aAnnotation) +
printExpected(properties) +
'\n' +
printLabel(bAnnotation) +
printReceived(received)
);
};
exports.printPropertiesAndReceived = printPropertiesAndReceived;
const MAX_DIFF_STRING_LENGTH = 20000;
const printSnapshotAndReceived = (a, b, received, expand) => {
const aAnnotation = 'Snapshot';
const bAnnotation = 'Received';
const aColor = aSnapshotColor;
const bColor = bReceivedColor;
const options = {
aAnnotation,
aColor,
bAnnotation,
bColor,
changeLineTrailingSpaceColor: noColor,
commonLineTrailingSpaceColor: _chalk.default.bgYellow,
emptyFirstOrLastLinePlaceholder: '↵',
// U+21B5
expand,
includeChangeCounts: true
};
if (typeof received === 'string') {
if (
a.length >= 2 &&
a.startsWith('"') &&
a.endsWith('"') &&
b === (0, _prettyFormat.default)(received)
) {
// If snapshot looks like default serialization of a string
// and received is string which has default serialization.
if (!a.includes('\n') && !b.includes('\n')) {
// If neither string is multiline,
// display as labels and quoted strings.
let aQuoted = a;
let bQuoted = b;
if (
a.length - 2 <= MAX_DIFF_STRING_LENGTH &&
b.length - 2 <= MAX_DIFF_STRING_LENGTH
) {
const diffs = (0, _jestDiff.diffStringsRaw)(
a.slice(1, -1),
b.slice(1, -1),
true
);
const hasCommon = diffs.some(
diff => diff[0] === _jestDiff.DIFF_EQUAL
);
aQuoted =
'"' + joinDiffs(diffs, _jestDiff.DIFF_DELETE, hasCommon) + '"';
bQuoted =
'"' + joinDiffs(diffs, _jestDiff.DIFF_INSERT, hasCommon) + '"';
}
const printLabel = (0, _jestMatcherUtils.getLabelPrinter)(
aAnnotation,
bAnnotation
);
return (
printLabel(aAnnotation) +
aColor(aQuoted) +
'\n' +
printLabel(bAnnotation) +
bColor(bQuoted)
);
} // Else either string is multiline, so display as unquoted strings.
a = (0, _utils2.deserializeString)(a); // hypothetical expected string
b = received; // not serialized
} // Else expected had custom serialization or was not a string
// or received has custom serialization.
return a.length <= MAX_DIFF_STRING_LENGTH &&
b.length <= MAX_DIFF_STRING_LENGTH
? (0, _jestDiff.diffStringsUnified)(a, b, options)
: (0, _jestDiff.diffLinesUnified)(a.split('\n'), b.split('\n'), options);
}
if (isLineDiffable(received)) {
const aLines2 = a.split('\n');
const bLines2 = b.split('\n'); // Fall through to fix a regression for custom serializers
// like jest-snapshot-serializer-raw that ignore the indent option.
const b0 = (0, _utils2.serialize)(received, 0);
if (b0 !== b) {
const aLines0 = (0, _dedentLines.dedentLines)(aLines2);
if (aLines0 !== null) {
// Compare lines without indentation.
const bLines0 = b0.split('\n');
return (0, _jestDiff.diffLinesUnified2)(
aLines2,
bLines2,
aLines0,
bLines0,
options
);
}
} // Fall back because:
// * props include a multiline string
// * text has more than one adjacent line
// * markup does not close
return (0, _jestDiff.diffLinesUnified)(aLines2, bLines2, options);
}
const printLabel = (0, _jestMatcherUtils.getLabelPrinter)(
aAnnotation,
bAnnotation
);
return (
printLabel(aAnnotation) +
aColor(a) +
'\n' +
printLabel(bAnnotation) +
bColor(b)
);
};
exports.printSnapshotAndReceived = printSnapshotAndReceived;