123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- 'use strict';
- Object.defineProperty(exports, '__esModule', {
- value: true
- });
- exports.saveInlineSnapshots = void 0;
- var _fs = _interopRequireDefault(require('fs'));
- var _path = _interopRequireDefault(require('path'));
- var _semver = _interopRequireDefault(require('semver'));
- var _types = require('@babel/types');
- var _utils = require('./utils');
- function _interopRequireDefault(obj) {
- return obj && obj.__esModule ? obj : {default: obj};
- }
- var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol;
- var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol;
- var jestWriteFile =
- global[Symbol.for('jest-native-write-file')] || _fs.default.writeFileSync;
- function _objectSpread(target) {
- for (var i = 1; i < arguments.length; i++) {
- var source = arguments[i] != null ? arguments[i] : {};
- var ownKeys = Object.keys(source);
- if (typeof Object.getOwnPropertySymbols === 'function') {
- ownKeys = ownKeys.concat(
- Object.getOwnPropertySymbols(source).filter(function(sym) {
- return Object.getOwnPropertyDescriptor(source, sym).enumerable;
- })
- );
- }
- ownKeys.forEach(function(key) {
- _defineProperty(target, key, source[key]);
- });
- }
- return target;
- }
- function _defineProperty(obj, key, value) {
- if (key in obj) {
- Object.defineProperty(obj, key, {
- value: value,
- enumerable: true,
- configurable: true,
- writable: true
- });
- } else {
- obj[key] = value;
- }
- return obj;
- }
- var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol;
- var jestReadFile =
- global[Symbol.for('jest-native-read-file')] || _fs.default.readFileSync;
- const saveInlineSnapshots = (snapshots, prettier, babelTraverse) => {
- if (!prettier) {
- throw new Error(
- `Jest: Inline Snapshots requires Prettier.\n` +
- `Please ensure "prettier" is installed in your project.`
- );
- } // Custom parser API was added in 1.5.0
- if (_semver.default.lt(prettier.version, '1.5.0')) {
- throw new Error(
- `Jest: Inline Snapshots require prettier>=1.5.0.\n` +
- `Please upgrade "prettier".`
- );
- }
- const snapshotsByFile = groupSnapshotsByFile(snapshots);
- var _arr = Object.keys(snapshotsByFile);
- for (var _i = 0; _i < _arr.length; _i++) {
- const sourceFilePath = _arr[_i];
- saveSnapshotsForFile(
- snapshotsByFile[sourceFilePath],
- sourceFilePath,
- prettier,
- babelTraverse
- );
- }
- };
- exports.saveInlineSnapshots = saveInlineSnapshots;
- const saveSnapshotsForFile = (
- snapshots,
- sourceFilePath,
- prettier,
- babelTraverse
- ) => {
- const sourceFile = jestReadFile(sourceFilePath, 'utf8'); // Resolve project configuration.
- // For older versions of Prettier, do not load configuration.
- const config = prettier.resolveConfig
- ? prettier.resolveConfig.sync(sourceFilePath, {
- editorconfig: true
- })
- : null; // Detect the parser for the test file.
- // For older versions of Prettier, fallback to a simple parser detection.
- const inferredParser = prettier.getFileInfo
- ? prettier.getFileInfo.sync(sourceFilePath).inferredParser
- : (config && config.parser) || simpleDetectParser(sourceFilePath); // Insert snapshots using the custom parser API. After insertion, the code is
- // formatted, except snapshot indentation. Snapshots cannot be formatted until
- // after the initial format because we don't know where the call expression
- // will be placed (specifically its indentation).
- const newSourceFile = prettier.format(
- sourceFile,
- _objectSpread({}, config, {
- filepath: sourceFilePath,
- parser: createInsertionParser(snapshots, inferredParser, babelTraverse)
- })
- ); // Format the snapshots using the custom parser API.
- const formattedNewSourceFile = prettier.format(
- newSourceFile,
- _objectSpread({}, config, {
- filepath: sourceFilePath,
- parser: createFormattingParser(inferredParser, babelTraverse)
- })
- );
- if (formattedNewSourceFile !== sourceFile) {
- jestWriteFile(sourceFilePath, formattedNewSourceFile);
- }
- };
- const groupSnapshotsBy = createKey => snapshots =>
- snapshots.reduce((object, inlineSnapshot) => {
- const key = createKey(inlineSnapshot);
- return _objectSpread({}, object, {
- [key]: (object[key] || []).concat(inlineSnapshot)
- });
- }, {});
- const groupSnapshotsByFrame = groupSnapshotsBy(({frame: {line, column}}) =>
- typeof line === 'number' && typeof column === 'number'
- ? `${line}:${column - 1}`
- : ''
- );
- const groupSnapshotsByFile = groupSnapshotsBy(({frame: {file}}) => file);
- const indent = (snapshot, numIndents, indentation) => {
- const lines = snapshot.split('\n'); // Prevent re-identation of inline snapshots.
- if (
- lines.length >= 2 &&
- lines[1].startsWith(indentation.repeat(numIndents + 1))
- ) {
- return snapshot;
- }
- return lines
- .map((line, index) => {
- if (index === 0) {
- // First line is either a 1-line snapshot or a blank line.
- return line;
- } else if (index !== lines.length - 1) {
- // Do not indent empty lines.
- if (line === '') {
- return line;
- } // Not last line, indent one level deeper than expect call.
- return indentation.repeat(numIndents + 1) + line;
- } else {
- // The last line should be placed on the same level as the expect call.
- return indentation.repeat(numIndents) + line;
- }
- })
- .join('\n');
- };
- const getAst = (parsers, inferredParser, text) => {
- // Flow uses a 'Program' parent node, babel expects a 'File'.
- let ast = parsers[inferredParser](text);
- if (ast.type !== 'File') {
- ast = (0, _types.file)(ast, ast.comments, ast.tokens);
- delete ast.program.comments;
- }
- return ast;
- }; // This parser inserts snapshots into the AST.
- const createInsertionParser = (snapshots, inferredParser, babelTraverse) => (
- text,
- parsers,
- options
- ) => {
- // Workaround for https://github.com/prettier/prettier/issues/3150
- options.parser = inferredParser;
- const groupedSnapshots = groupSnapshotsByFrame(snapshots);
- const remainingSnapshots = new Set(snapshots.map(({snapshot}) => snapshot));
- const ast = getAst(parsers, inferredParser, text);
- babelTraverse(ast, {
- CallExpression({node: {arguments: args, callee}}) {
- if (
- callee.type !== 'MemberExpression' ||
- callee.property.type !== 'Identifier'
- ) {
- return;
- }
- const _callee$property$loc$ = callee.property.loc.start,
- line = _callee$property$loc$.line,
- column = _callee$property$loc$.column;
- const snapshotsForFrame = groupedSnapshots[`${line}:${column}`];
- if (!snapshotsForFrame) {
- return;
- }
- if (snapshotsForFrame.length > 1) {
- throw new Error(
- 'Jest: Multiple inline snapshots for the same call are not supported.'
- );
- }
- const snapshotIndex = args.findIndex(
- ({type}) => type === 'TemplateLiteral'
- );
- const values = snapshotsForFrame.map(({snapshot}) => {
- remainingSnapshots.delete(snapshot);
- return (0, _types.templateLiteral)(
- [
- (0, _types.templateElement)({
- raw: (0, _utils.escapeBacktickString)(snapshot)
- })
- ],
- []
- );
- });
- const replacementNode = values[0];
- if (snapshotIndex > -1) {
- args[snapshotIndex] = replacementNode;
- } else {
- args.push(replacementNode);
- }
- }
- });
- if (remainingSnapshots.size) {
- throw new Error(`Jest: Couldn't locate all inline snapshots.`);
- }
- return ast;
- }; // This parser formats snapshots to the correct indentation.
- const createFormattingParser = (inferredParser, babelTraverse) => (
- text,
- parsers,
- options
- ) => {
- // Workaround for https://github.com/prettier/prettier/issues/3150
- options.parser = inferredParser;
- const ast = getAst(parsers, inferredParser, text);
- babelTraverse(ast, {
- CallExpression({node: {arguments: args, callee}}) {
- if (
- callee.type !== 'MemberExpression' ||
- callee.property.type !== 'Identifier' ||
- callee.property.name !== 'toMatchInlineSnapshot' ||
- !callee.loc ||
- callee.computed
- ) {
- return;
- }
- let snapshotIndex;
- let snapshot;
- for (let i = 0; i < args.length; i++) {
- const node = args[i];
- if (node.type === 'TemplateLiteral') {
- snapshotIndex = i;
- snapshot = node.quasis[0].value.raw;
- }
- }
- if (snapshot === undefined || snapshotIndex === undefined) {
- return;
- }
- const useSpaces = !options.useTabs;
- snapshot = indent(
- snapshot,
- Math.ceil(
- useSpaces
- ? callee.loc.start.column / options.tabWidth
- : callee.loc.start.column / 2 // Each tab is 2 characters.
- ),
- useSpaces ? ' '.repeat(options.tabWidth) : '\t'
- );
- const replacementNode = (0, _types.templateLiteral)(
- [
- (0, _types.templateElement)({
- raw: snapshot
- })
- ],
- []
- );
- args[snapshotIndex] = replacementNode;
- }
- });
- return ast;
- };
- const simpleDetectParser = filePath => {
- const extname = _path.default.extname(filePath);
- if (/tsx?$/.test(extname)) {
- return 'typescript';
- }
- return 'babylon';
- };
|