State.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. var fs = _interopRequireWildcard(require('graceful-fs'));
  7. var _jestMessageUtil = require('jest-message-util');
  8. var _InlineSnapshots = require('./InlineSnapshots');
  9. var _utils = require('./utils');
  10. function _getRequireWildcardCache() {
  11. if (typeof WeakMap !== 'function') return null;
  12. var cache = new WeakMap();
  13. _getRequireWildcardCache = function () {
  14. return cache;
  15. };
  16. return cache;
  17. }
  18. function _interopRequireWildcard(obj) {
  19. if (obj && obj.__esModule) {
  20. return obj;
  21. }
  22. if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) {
  23. return {default: obj};
  24. }
  25. var cache = _getRequireWildcardCache();
  26. if (cache && cache.has(obj)) {
  27. return cache.get(obj);
  28. }
  29. var newObj = {};
  30. var hasPropertyDescriptor =
  31. Object.defineProperty && Object.getOwnPropertyDescriptor;
  32. for (var key in obj) {
  33. if (Object.prototype.hasOwnProperty.call(obj, key)) {
  34. var desc = hasPropertyDescriptor
  35. ? Object.getOwnPropertyDescriptor(obj, key)
  36. : null;
  37. if (desc && (desc.get || desc.set)) {
  38. Object.defineProperty(newObj, key, desc);
  39. } else {
  40. newObj[key] = obj[key];
  41. }
  42. }
  43. }
  44. newObj.default = obj;
  45. if (cache) {
  46. cache.set(obj, newObj);
  47. }
  48. return newObj;
  49. }
  50. var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol;
  51. var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol;
  52. var jestExistsFile =
  53. global[Symbol.for('jest-native-exists-file')] || fs.existsSync;
  54. function _defineProperty(obj, key, value) {
  55. if (key in obj) {
  56. Object.defineProperty(obj, key, {
  57. value: value,
  58. enumerable: true,
  59. configurable: true,
  60. writable: true
  61. });
  62. } else {
  63. obj[key] = value;
  64. }
  65. return obj;
  66. }
  67. class SnapshotState {
  68. // @ts-expect-error
  69. constructor(snapshotPath, options) {
  70. _defineProperty(this, '_counters', void 0);
  71. _defineProperty(this, '_dirty', void 0);
  72. _defineProperty(this, '_index', void 0);
  73. _defineProperty(this, '_updateSnapshot', void 0);
  74. _defineProperty(this, '_snapshotData', void 0);
  75. _defineProperty(this, '_initialData', void 0);
  76. _defineProperty(this, '_snapshotPath', void 0);
  77. _defineProperty(this, '_inlineSnapshots', void 0);
  78. _defineProperty(this, '_uncheckedKeys', void 0);
  79. _defineProperty(this, '_getBabelTraverse', void 0);
  80. _defineProperty(this, '_getPrettier', void 0);
  81. _defineProperty(this, 'added', void 0);
  82. _defineProperty(this, 'expand', void 0);
  83. _defineProperty(this, 'matched', void 0);
  84. _defineProperty(this, 'unmatched', void 0);
  85. _defineProperty(this, 'updated', void 0);
  86. this._snapshotPath = snapshotPath;
  87. const {data, dirty} = (0, _utils.getSnapshotData)(
  88. this._snapshotPath,
  89. options.updateSnapshot
  90. );
  91. this._initialData = data;
  92. this._snapshotData = data;
  93. this._dirty = dirty;
  94. this._getBabelTraverse = options.getBabelTraverse;
  95. this._getPrettier = options.getPrettier;
  96. this._inlineSnapshots = [];
  97. this._uncheckedKeys = new Set(Object.keys(this._snapshotData));
  98. this._counters = new Map();
  99. this._index = 0;
  100. this.expand = options.expand || false;
  101. this.added = 0;
  102. this.matched = 0;
  103. this.unmatched = 0;
  104. this._updateSnapshot = options.updateSnapshot;
  105. this.updated = 0;
  106. }
  107. markSnapshotsAsCheckedForTest(testName) {
  108. this._uncheckedKeys.forEach(uncheckedKey => {
  109. if ((0, _utils.keyToTestName)(uncheckedKey) === testName) {
  110. this._uncheckedKeys.delete(uncheckedKey);
  111. }
  112. });
  113. }
  114. _addSnapshot(key, receivedSerialized, options) {
  115. this._dirty = true;
  116. if (options.isInline) {
  117. const error = options.error || new Error();
  118. const lines = (0, _jestMessageUtil.getStackTraceLines)(
  119. (0, _utils.removeLinesBeforeExternalMatcherTrap)(error.stack || '')
  120. );
  121. const frame = (0, _jestMessageUtil.getTopFrame)(lines);
  122. if (!frame) {
  123. throw new Error(
  124. "Jest: Couldn't infer stack frame for inline snapshot."
  125. );
  126. }
  127. this._inlineSnapshots.push({
  128. frame,
  129. snapshot: receivedSerialized
  130. });
  131. } else {
  132. this._snapshotData[key] = receivedSerialized;
  133. }
  134. }
  135. clear() {
  136. this._snapshotData = this._initialData;
  137. this._inlineSnapshots = [];
  138. this._counters = new Map();
  139. this._index = 0;
  140. this.added = 0;
  141. this.matched = 0;
  142. this.unmatched = 0;
  143. this.updated = 0;
  144. }
  145. save() {
  146. const hasExternalSnapshots = Object.keys(this._snapshotData).length;
  147. const hasInlineSnapshots = this._inlineSnapshots.length;
  148. const isEmpty = !hasExternalSnapshots && !hasInlineSnapshots;
  149. const status = {
  150. deleted: false,
  151. saved: false
  152. };
  153. if ((this._dirty || this._uncheckedKeys.size) && !isEmpty) {
  154. if (hasExternalSnapshots) {
  155. (0, _utils.saveSnapshotFile)(this._snapshotData, this._snapshotPath);
  156. }
  157. if (hasInlineSnapshots) {
  158. const prettier = this._getPrettier(); // Load lazily
  159. const babelTraverse = this._getBabelTraverse(); // Load lazily
  160. (0, _InlineSnapshots.saveInlineSnapshots)(
  161. this._inlineSnapshots,
  162. prettier,
  163. babelTraverse
  164. );
  165. }
  166. status.saved = true;
  167. } else if (!hasExternalSnapshots && jestExistsFile(this._snapshotPath)) {
  168. if (this._updateSnapshot === 'all') {
  169. fs.unlinkSync(this._snapshotPath);
  170. }
  171. status.deleted = true;
  172. }
  173. return status;
  174. }
  175. getUncheckedCount() {
  176. return this._uncheckedKeys.size || 0;
  177. }
  178. getUncheckedKeys() {
  179. return Array.from(this._uncheckedKeys);
  180. }
  181. removeUncheckedKeys() {
  182. if (this._updateSnapshot === 'all' && this._uncheckedKeys.size) {
  183. this._dirty = true;
  184. this._uncheckedKeys.forEach(key => delete this._snapshotData[key]);
  185. this._uncheckedKeys.clear();
  186. }
  187. }
  188. match({testName, received, key, inlineSnapshot, isInline, error}) {
  189. this._counters.set(testName, (this._counters.get(testName) || 0) + 1);
  190. const count = Number(this._counters.get(testName));
  191. if (!key) {
  192. key = (0, _utils.testNameToKey)(testName, count);
  193. } // Do not mark the snapshot as "checked" if the snapshot is inline and
  194. // there's an external snapshot. This way the external snapshot can be
  195. // removed with `--updateSnapshot`.
  196. if (!(isInline && this._snapshotData[key] !== undefined)) {
  197. this._uncheckedKeys.delete(key);
  198. }
  199. const receivedSerialized = (0, _utils.addExtraLineBreaks)(
  200. (0, _utils.serialize)(received)
  201. );
  202. const expected = isInline ? inlineSnapshot : this._snapshotData[key];
  203. const pass = expected === receivedSerialized;
  204. const hasSnapshot = expected !== undefined;
  205. const snapshotIsPersisted = isInline || fs.existsSync(this._snapshotPath);
  206. if (pass && !isInline) {
  207. // Executing a snapshot file as JavaScript and writing the strings back
  208. // when other snapshots have changed loses the proper escaping for some
  209. // characters. Since we check every snapshot in every test, use the newly
  210. // generated formatted string.
  211. // Note that this is only relevant when a snapshot is added and the dirty
  212. // flag is set.
  213. this._snapshotData[key] = receivedSerialized;
  214. } // These are the conditions on when to write snapshots:
  215. // * There's no snapshot file in a non-CI environment.
  216. // * There is a snapshot file and we decided to update the snapshot.
  217. // * There is a snapshot file, but it doesn't have this snaphsot.
  218. // These are the conditions on when not to write snapshots:
  219. // * The update flag is set to 'none'.
  220. // * There's no snapshot file or a file without this snapshot on a CI environment.
  221. if (
  222. (hasSnapshot && this._updateSnapshot === 'all') ||
  223. ((!hasSnapshot || !snapshotIsPersisted) &&
  224. (this._updateSnapshot === 'new' || this._updateSnapshot === 'all'))
  225. ) {
  226. if (this._updateSnapshot === 'all') {
  227. if (!pass) {
  228. if (hasSnapshot) {
  229. this.updated++;
  230. } else {
  231. this.added++;
  232. }
  233. this._addSnapshot(key, receivedSerialized, {
  234. error,
  235. isInline
  236. });
  237. } else {
  238. this.matched++;
  239. }
  240. } else {
  241. this._addSnapshot(key, receivedSerialized, {
  242. error,
  243. isInline
  244. });
  245. this.added++;
  246. }
  247. return {
  248. actual: '',
  249. count,
  250. expected: '',
  251. key,
  252. pass: true
  253. };
  254. } else {
  255. if (!pass) {
  256. this.unmatched++;
  257. return {
  258. actual: (0, _utils.removeExtraLineBreaks)(receivedSerialized),
  259. count,
  260. expected:
  261. expected !== undefined
  262. ? (0, _utils.removeExtraLineBreaks)(expected)
  263. : undefined,
  264. key,
  265. pass: false
  266. };
  267. } else {
  268. this.matched++;
  269. return {
  270. actual: '',
  271. count,
  272. expected: '',
  273. key,
  274. pass: true
  275. };
  276. }
  277. }
  278. }
  279. fail(testName, _received, key) {
  280. this._counters.set(testName, (this._counters.get(testName) || 0) + 1);
  281. const count = Number(this._counters.get(testName));
  282. if (!key) {
  283. key = (0, _utils.testNameToKey)(testName, count);
  284. }
  285. this._uncheckedKeys.delete(key);
  286. this.unmatched++;
  287. return key;
  288. }
  289. }
  290. exports.default = SnapshotState;