printSnapshot.js 11 KB

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