State.js 8.4 KB

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