State.js 7.3 KB

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