printSnapshot.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', {
  3. value: true
  4. });
  5. exports.printSnapshotAndReceived =
  6. exports.printReceived =
  7. exports.printPropertiesAndReceived =
  8. exports.printExpected =
  9. exports.noColor =
  10. exports.matcherHintFromConfig =
  11. exports.getSnapshotColorForChalkInstance =
  12. exports.getReceivedColorForChalkInstance =
  13. exports.bReceivedColor =
  14. exports.aSnapshotColor =
  15. exports.SNAPSHOT_ARG =
  16. exports.PROPERTIES_ARG =
  17. exports.HINT_ARG =
  18. void 0;
  19. var _chalk = _interopRequireDefault(require('chalk'));
  20. var _expectUtils = require('@jest/expect-utils');
  21. var _jestDiff = require('jest-diff');
  22. var _jestGetType = require('jest-get-type');
  23. var _jestMatcherUtils = require('jest-matcher-utils');
  24. var _prettyFormat = require('pretty-format');
  25. var _colors = require('./colors');
  26. var _dedentLines = require('./dedentLines');
  27. var _utils = require('./utils');
  28. function _interopRequireDefault(obj) {
  29. return obj && obj.__esModule ? obj : {default: obj};
  30. }
  31. /**
  32. * Copyright (c) Meta Platforms, Inc. and affiliates.
  33. *
  34. * This source code is licensed under the MIT license found in the
  35. * LICENSE file in the root directory of this source tree.
  36. */
  37. const getSnapshotColorForChalkInstance = chalkInstance => {
  38. const level = chalkInstance.level;
  39. if (level === 3) {
  40. return chalkInstance
  41. .rgb(
  42. _colors.aForeground3[0],
  43. _colors.aForeground3[1],
  44. _colors.aForeground3[2]
  45. )
  46. .bgRgb(
  47. _colors.aBackground3[0],
  48. _colors.aBackground3[1],
  49. _colors.aBackground3[2]
  50. );
  51. }
  52. if (level === 2) {
  53. return chalkInstance
  54. .ansi256(_colors.aForeground2)
  55. .bgAnsi256(_colors.aBackground2);
  56. }
  57. return chalkInstance.magenta.bgYellowBright;
  58. };
  59. exports.getSnapshotColorForChalkInstance = getSnapshotColorForChalkInstance;
  60. const getReceivedColorForChalkInstance = chalkInstance => {
  61. const level = chalkInstance.level;
  62. if (level === 3) {
  63. return chalkInstance
  64. .rgb(
  65. _colors.bForeground3[0],
  66. _colors.bForeground3[1],
  67. _colors.bForeground3[2]
  68. )
  69. .bgRgb(
  70. _colors.bBackground3[0],
  71. _colors.bBackground3[1],
  72. _colors.bBackground3[2]
  73. );
  74. }
  75. if (level === 2) {
  76. return chalkInstance
  77. .ansi256(_colors.bForeground2)
  78. .bgAnsi256(_colors.bBackground2);
  79. }
  80. return chalkInstance.cyan.bgWhiteBright; // also known as teal
  81. };
  82. exports.getReceivedColorForChalkInstance = getReceivedColorForChalkInstance;
  83. const aSnapshotColor = getSnapshotColorForChalkInstance(_chalk.default);
  84. exports.aSnapshotColor = aSnapshotColor;
  85. const bReceivedColor = getReceivedColorForChalkInstance(_chalk.default);
  86. exports.bReceivedColor = bReceivedColor;
  87. const noColor = string => string;
  88. exports.noColor = noColor;
  89. const HINT_ARG = 'hint';
  90. exports.HINT_ARG = HINT_ARG;
  91. const SNAPSHOT_ARG = 'snapshot';
  92. exports.SNAPSHOT_ARG = SNAPSHOT_ARG;
  93. const PROPERTIES_ARG = 'properties';
  94. exports.PROPERTIES_ARG = PROPERTIES_ARG;
  95. const matcherHintFromConfig = (
  96. {context: {isNot, promise}, hint, inlineSnapshot, matcherName, properties},
  97. isUpdatable
  98. ) => {
  99. const options = {
  100. isNot,
  101. promise
  102. };
  103. if (isUpdatable) {
  104. options.receivedColor = bReceivedColor;
  105. }
  106. let expectedArgument = '';
  107. if (typeof properties === 'object') {
  108. expectedArgument = PROPERTIES_ARG;
  109. if (isUpdatable) {
  110. options.expectedColor = noColor;
  111. }
  112. if (typeof hint === 'string' && hint.length !== 0) {
  113. options.secondArgument = HINT_ARG;
  114. options.secondArgumentColor = _jestMatcherUtils.BOLD_WEIGHT;
  115. } else if (typeof inlineSnapshot === 'string') {
  116. options.secondArgument = SNAPSHOT_ARG;
  117. if (isUpdatable) {
  118. options.secondArgumentColor = aSnapshotColor;
  119. } else {
  120. options.secondArgumentColor = noColor;
  121. }
  122. }
  123. } else {
  124. if (typeof hint === 'string' && hint.length !== 0) {
  125. expectedArgument = HINT_ARG;
  126. options.expectedColor = _jestMatcherUtils.BOLD_WEIGHT;
  127. } else if (typeof inlineSnapshot === 'string') {
  128. expectedArgument = SNAPSHOT_ARG;
  129. if (isUpdatable) {
  130. options.expectedColor = aSnapshotColor;
  131. }
  132. }
  133. }
  134. return (0, _jestMatcherUtils.matcherHint)(
  135. matcherName,
  136. undefined,
  137. expectedArgument,
  138. options
  139. );
  140. };
  141. // Given array of diffs, return string:
  142. // * include common substrings
  143. // * exclude change substrings which have opposite op
  144. // * include change substrings which have argument op
  145. // with change color only if there is a common substring
  146. exports.matcherHintFromConfig = matcherHintFromConfig;
  147. const joinDiffs = (diffs, op, hasCommon) =>
  148. diffs.reduce(
  149. (reduced, diff) =>
  150. reduced +
  151. (diff[0] === _jestDiff.DIFF_EQUAL
  152. ? diff[1]
  153. : diff[0] !== op
  154. ? ''
  155. : hasCommon
  156. ? (0, _jestMatcherUtils.INVERTED_COLOR)(diff[1])
  157. : diff[1]),
  158. ''
  159. );
  160. const isLineDiffable = received => {
  161. const receivedType = (0, _jestGetType.getType)(received);
  162. if ((0, _jestGetType.isPrimitive)(received)) {
  163. return typeof received === 'string';
  164. }
  165. if (
  166. receivedType === 'date' ||
  167. receivedType === 'function' ||
  168. receivedType === 'regexp'
  169. ) {
  170. return false;
  171. }
  172. if (received instanceof Error) {
  173. return false;
  174. }
  175. if (
  176. receivedType === 'object' &&
  177. typeof received.asymmetricMatch === 'function'
  178. ) {
  179. return false;
  180. }
  181. return true;
  182. };
  183. const printExpected = val =>
  184. (0, _jestMatcherUtils.EXPECTED_COLOR)((0, _utils.minify)(val));
  185. exports.printExpected = printExpected;
  186. const printReceived = val =>
  187. (0, _jestMatcherUtils.RECEIVED_COLOR)((0, _utils.minify)(val));
  188. exports.printReceived = printReceived;
  189. const printPropertiesAndReceived = (
  190. properties,
  191. received,
  192. expand // CLI options: true if `--expand` or false if `--no-expand`
  193. ) => {
  194. const aAnnotation = 'Expected properties';
  195. const bAnnotation = 'Received value';
  196. if (isLineDiffable(properties) && isLineDiffable(received)) {
  197. const {replacedExpected, replacedReceived} = (0,
  198. _jestMatcherUtils.replaceMatchedToAsymmetricMatcher)(
  199. properties,
  200. received,
  201. [],
  202. []
  203. );
  204. return (0, _jestDiff.diffLinesUnified)(
  205. (0, _utils.serialize)(replacedExpected).split('\n'),
  206. (0, _utils.serialize)(
  207. (0, _expectUtils.getObjectSubset)(replacedReceived, replacedExpected)
  208. ).split('\n'),
  209. {
  210. aAnnotation,
  211. aColor: _jestMatcherUtils.EXPECTED_COLOR,
  212. bAnnotation,
  213. bColor: _jestMatcherUtils.RECEIVED_COLOR,
  214. changeLineTrailingSpaceColor: _chalk.default.bgYellow,
  215. commonLineTrailingSpaceColor: _chalk.default.bgYellow,
  216. emptyFirstOrLastLinePlaceholder: '↵',
  217. // U+21B5
  218. expand,
  219. includeChangeCounts: true
  220. }
  221. );
  222. }
  223. const printLabel = (0, _jestMatcherUtils.getLabelPrinter)(
  224. aAnnotation,
  225. bAnnotation
  226. );
  227. return `${printLabel(aAnnotation) + printExpected(properties)}\n${printLabel(
  228. bAnnotation
  229. )}${printReceived(received)}`;
  230. };
  231. exports.printPropertiesAndReceived = printPropertiesAndReceived;
  232. const MAX_DIFF_STRING_LENGTH = 20000;
  233. const printSnapshotAndReceived = (a, b, received, expand, snapshotFormat) => {
  234. const aAnnotation = 'Snapshot';
  235. const bAnnotation = 'Received';
  236. const aColor = aSnapshotColor;
  237. const bColor = bReceivedColor;
  238. const options = {
  239. aAnnotation,
  240. aColor,
  241. bAnnotation,
  242. bColor,
  243. changeLineTrailingSpaceColor: noColor,
  244. commonLineTrailingSpaceColor: _chalk.default.bgYellow,
  245. emptyFirstOrLastLinePlaceholder: '↵',
  246. // U+21B5
  247. expand,
  248. includeChangeCounts: true
  249. };
  250. if (typeof received === 'string') {
  251. if (
  252. a.length >= 2 &&
  253. a.startsWith('"') &&
  254. a.endsWith('"') &&
  255. b === (0, _prettyFormat.format)(received)
  256. ) {
  257. // If snapshot looks like default serialization of a string
  258. // and received is string which has default serialization.
  259. if (!a.includes('\n') && !b.includes('\n')) {
  260. // If neither string is multiline,
  261. // display as labels and quoted strings.
  262. let aQuoted = a;
  263. let bQuoted = b;
  264. if (
  265. a.length - 2 <= MAX_DIFF_STRING_LENGTH &&
  266. b.length - 2 <= MAX_DIFF_STRING_LENGTH
  267. ) {
  268. const diffs = (0, _jestDiff.diffStringsRaw)(
  269. a.slice(1, -1),
  270. b.slice(1, -1),
  271. true
  272. );
  273. const hasCommon = diffs.some(
  274. diff => diff[0] === _jestDiff.DIFF_EQUAL
  275. );
  276. aQuoted = `"${joinDiffs(diffs, _jestDiff.DIFF_DELETE, hasCommon)}"`;
  277. bQuoted = `"${joinDiffs(diffs, _jestDiff.DIFF_INSERT, hasCommon)}"`;
  278. }
  279. const printLabel = (0, _jestMatcherUtils.getLabelPrinter)(
  280. aAnnotation,
  281. bAnnotation
  282. );
  283. return `${printLabel(aAnnotation) + aColor(aQuoted)}\n${printLabel(
  284. bAnnotation
  285. )}${bColor(bQuoted)}`;
  286. }
  287. // Else either string is multiline, so display as unquoted strings.
  288. a = (0, _utils.deserializeString)(a); // hypothetical expected string
  289. b = received; // not serialized
  290. }
  291. // Else expected had custom serialization or was not a string
  292. // or received has custom serialization.
  293. return a.length <= MAX_DIFF_STRING_LENGTH &&
  294. b.length <= MAX_DIFF_STRING_LENGTH
  295. ? (0, _jestDiff.diffStringsUnified)(a, b, options)
  296. : (0, _jestDiff.diffLinesUnified)(a.split('\n'), b.split('\n'), options);
  297. }
  298. if (isLineDiffable(received)) {
  299. const aLines2 = a.split('\n');
  300. const bLines2 = b.split('\n');
  301. // Fall through to fix a regression for custom serializers
  302. // like jest-snapshot-serializer-raw that ignore the indent option.
  303. const b0 = (0, _utils.serialize)(received, 0, snapshotFormat);
  304. if (b0 !== b) {
  305. const aLines0 = (0, _dedentLines.dedentLines)(aLines2);
  306. if (aLines0 !== null) {
  307. // Compare lines without indentation.
  308. const bLines0 = b0.split('\n');
  309. return (0, _jestDiff.diffLinesUnified2)(
  310. aLines2,
  311. bLines2,
  312. aLines0,
  313. bLines0,
  314. options
  315. );
  316. }
  317. }
  318. // Fall back because:
  319. // * props include a multiline string
  320. // * text has more than one adjacent line
  321. // * markup does not close
  322. return (0, _jestDiff.diffLinesUnified)(aLines2, bLines2, options);
  323. }
  324. const printLabel = (0, _jestMatcherUtils.getLabelPrinter)(
  325. aAnnotation,
  326. bAnnotation
  327. );
  328. return `${printLabel(aAnnotation) + aColor(a)}\n${printLabel(
  329. bAnnotation
  330. )}${bColor(b)}`;
  331. };
  332. exports.printSnapshotAndReceived = printSnapshotAndReceived;