inline_snapshots.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', {
  3. value: true
  4. });
  5. exports.saveInlineSnapshots = undefined;
  6. var _fs = require('fs');
  7. var _fs2 = _interopRequireDefault(_fs);
  8. var _semver = require('semver');
  9. var _semver2 = _interopRequireDefault(_semver);
  10. var _path = require('path');
  11. var _path2 = _interopRequireDefault(_path);
  12. var _babelTypes = require('babel-types');
  13. var _utils = require('./utils');
  14. function _interopRequireDefault(obj) {
  15. return obj && obj.__esModule ? obj : {default: obj};
  16. }
  17. const saveInlineSnapshots = (exports.saveInlineSnapshots = (
  18. snapshots,
  19. prettier,
  20. babelTraverse
  21. ) => {
  22. if (!prettier) {
  23. throw new Error(
  24. `Jest: Inline Snapshots requires Prettier.\n` +
  25. `Please ensure "prettier" is installed in your project.`
  26. );
  27. }
  28. // Custom parser API was added in 1.5.0
  29. if (_semver2.default.lt(prettier.version, '1.5.0')) {
  30. throw new Error(
  31. `Jest: Inline Snapshots require prettier>=1.5.0.\n` +
  32. `Please upgrade "prettier".`
  33. );
  34. }
  35. const snapshotsByFile = groupSnapshotsByFile(snapshots);
  36. for (const sourceFilePath of Object.keys(snapshotsByFile)) {
  37. saveSnapshotsForFile(
  38. snapshotsByFile[sourceFilePath],
  39. sourceFilePath,
  40. prettier,
  41. babelTraverse
  42. );
  43. }
  44. });
  45. /**
  46. * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
  47. *
  48. * This source code is licensed under the MIT license found in the
  49. * LICENSE file in the root directory of this source tree.
  50. *
  51. *
  52. */
  53. const saveSnapshotsForFile = (
  54. snapshots,
  55. sourceFilePath,
  56. prettier,
  57. babelTraverse
  58. ) => {
  59. const sourceFile = _fs2.default.readFileSync(sourceFilePath, 'utf8');
  60. // Resolve project configuration.
  61. // For older versions of Prettier, do not load configuration.
  62. const config = prettier.resolveConfig
  63. ? prettier.resolveConfig.sync(sourceFilePath, {
  64. editorconfig: true
  65. })
  66. : null;
  67. // Detect the parser for the test file.
  68. // For older versions of Prettier, fallback to a simple parser detection.
  69. const inferredParser = prettier.getFileInfo
  70. ? prettier.getFileInfo.sync(sourceFilePath).inferredParser
  71. : (config && config.parser) || simpleDetectParser(sourceFilePath);
  72. // Format the source code using the custom parser API.
  73. const newSourceFile = prettier.format(
  74. sourceFile,
  75. Object.assign({}, config, {
  76. filepath: sourceFilePath,
  77. parser: createParser(snapshots, inferredParser, babelTraverse)
  78. })
  79. );
  80. if (newSourceFile !== sourceFile) {
  81. _fs2.default.writeFileSync(sourceFilePath, newSourceFile);
  82. }
  83. };
  84. const groupSnapshotsBy = createKey => snapshots =>
  85. snapshots.reduce((object, inlineSnapshot) => {
  86. const key = createKey(inlineSnapshot);
  87. return Object.assign(object, {
  88. [key]: (object[key] || []).concat(inlineSnapshot)
  89. });
  90. }, {});
  91. const groupSnapshotsByFrame = groupSnapshotsBy(_ref => {
  92. var _ref$frame = _ref.frame;
  93. let line = _ref$frame.line,
  94. column = _ref$frame.column;
  95. return `${line}:${column - 1}`;
  96. });
  97. const groupSnapshotsByFile = groupSnapshotsBy(_ref2 => {
  98. let file = _ref2.frame.file;
  99. return file;
  100. });
  101. const createParser = (snapshots, inferredParser, babelTraverse) => (
  102. text,
  103. parsers,
  104. options
  105. ) => {
  106. // Workaround for https://github.com/prettier/prettier/issues/3150
  107. options.parser = inferredParser;
  108. const groupedSnapshots = groupSnapshotsByFrame(snapshots);
  109. const remainingSnapshots = new Set(
  110. snapshots.map(_ref3 => {
  111. let snapshot = _ref3.snapshot;
  112. return snapshot;
  113. })
  114. );
  115. let ast = parsers[inferredParser](text);
  116. // Flow uses a 'Program' parent node, babel expects a 'File'.
  117. if (ast.type !== 'File') {
  118. ast = (0, _babelTypes.file)(ast, ast.comments, ast.tokens);
  119. delete ast.program.comments;
  120. }
  121. babelTraverse(ast, {
  122. CallExpression: function(_ref4) {
  123. var _ref4$node = _ref4.node;
  124. let args = _ref4$node.arguments,
  125. callee = _ref4$node.callee;
  126. if (
  127. callee.type !== 'MemberExpression' ||
  128. callee.property.type !== 'Identifier'
  129. ) {
  130. return;
  131. }
  132. var _callee$property$loc$ = callee.property.loc.start;
  133. const line = _callee$property$loc$.line,
  134. column = _callee$property$loc$.column;
  135. const snapshotsForFrame = groupedSnapshots[`${line}:${column}`];
  136. if (!snapshotsForFrame) {
  137. return;
  138. }
  139. if (snapshotsForFrame.length > 1) {
  140. throw new Error(
  141. 'Jest: Multiple inline snapshots for the same call are not supported.'
  142. );
  143. }
  144. const snapshotIndex = args.findIndex(_ref5 => {
  145. let type = _ref5.type;
  146. return type === 'TemplateLiteral';
  147. });
  148. const values = snapshotsForFrame.map(_ref6 => {
  149. let snapshot = _ref6.snapshot;
  150. remainingSnapshots.delete(snapshot);
  151. return (0, _babelTypes.templateLiteral)(
  152. [
  153. (0, _babelTypes.templateElement)({
  154. raw: (0, _utils.escapeBacktickString)(snapshot)
  155. })
  156. ],
  157. []
  158. );
  159. });
  160. const replacementNode = values[0];
  161. if (snapshotIndex > -1) {
  162. args[snapshotIndex] = replacementNode;
  163. } else {
  164. args.push(replacementNode);
  165. }
  166. }
  167. });
  168. if (remainingSnapshots.size) {
  169. throw new Error(`Jest: Couldn't locate all inline snapshots.`);
  170. }
  171. return ast;
  172. };
  173. const simpleDetectParser = filePath => {
  174. const extname = _path2.default.extname(filePath);
  175. if (/tsx?$/.test(extname)) {
  176. return 'typescript';
  177. }
  178. return 'babylon';
  179. };