printSnapshot.js 10 KB

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