setup-python/node_modules/jest-snapshot/build/inline_snapshots.js
Danny McCormick 39c08a0eaa Initial pass
2019-06-26 21:12:00 -04:00

323 lines
9 KiB
JavaScript

'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');
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';
};