UNPKG

jest-codemods

Version:

Codemods for migrating test files to Jest

228 lines (227 loc) 8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.rewriteAssertionsAndTestArgument = rewriteAssertionsAndTestArgument; exports.rewriteDestructuredTArgument = rewriteDestructuredTArgument; exports.renameExecutionInterface = renameExecutionInterface; exports.detectUnsupportedNaming = detectUnsupportedNaming; var logger_1 = __importDefault(require("./logger")); /** * Rewrite last argument of a given CallExpression path * @param {jscodeshift} j * @param {CallExpression} path * @param {string} newArgument */ function renameTestFunctionArgument(j, path, newArgument) { var lastArg = path.node.arguments[path.node.arguments.length - 1]; if (!lastArg) { return; } if (lastArg.type === 'ArrowFunctionExpression') { var arrowFunction = j.arrowFunctionExpression([j.identifier(newArgument === '' ? '()' : newArgument)], lastArg.body, false); arrowFunction.async = lastArg.async; path.node.arguments[path.node.arguments.length - 1] = arrowFunction; } else if (lastArg.type === 'FunctionExpression') { lastArg.params = [j.identifier(newArgument)]; } } /** * Rewrite Tape or AVA failing assertion (t.fail) * @param {jscodeshift} j * @param {CallExpression} path * @return {boolean} if any paths were changed */ function rewriteFailingAssertion(j, path) { return (j(path) .find(j.CallExpression, { callee: { object: { name: 't' }, property: { name: 'fail' }, }, }) .forEach(function (pFail) { pFail.node.callee = j.identifier('done.fail'); }) .size() > 0); } /** * Rewrite Tape or AVA async callbacks (t.end) * @param {jscodeshift} j * @param {CallExpression} path * @return {boolean} if any paths were changed */ function rewriteEndCallback(j, path) { // calls to t.end() var containsCalls = j(path) .find(j.CallExpression, { callee: { object: { name: 't' }, property: { name: 'end' }, }, }) .filter(function (p) { // if t.end is in the scope of the test function we remove it var outerParent = p.parent.parent.parent.node; var inTestScope = outerParent.params && outerParent.params[0] && outerParent.params[0].name === 't'; if (inTestScope) { p.prune(); return null; } // else it might be used for async testing. We rename it to // familiar Jasmine 'done()' p.node.callee = j.identifier('done'); return true; }) .size() > 0; // references to t.end var containsReference = j(path) .find(j.MemberExpression, { object: { name: 't' }, property: { name: 'end' }, }) .replaceWith(j.identifier('done')) .size() > 0; return containsCalls || containsReference; } /** * Remove t.pass (no equivalent in Jest) * @param {jscodeshift} j * @param {CallExpression} path */ function removePassingAssertion(j, path) { // t.pass is a no op j(path) .find(j.CallExpression, { callee: { object: { name: 't' }, property: { name: 'pass' }, }, }) .remove(); } /** * Rewrites CallExpression by: * - removing 't.pass' calls * - 't.end' calls are possible remove or renamed to 'done' * - 't.fail' calls are changed to 'done.fail' * * @param {jscodeshift} j * @param {CallExpression} path of test function */ function rewriteAssertionsAndTestArgument(j, path) { var containsFailCalls = rewriteFailingAssertion(j, path); var containsEndCalls = rewriteEndCallback(j, path); removePassingAssertion(j, path); var argumentName = containsEndCalls || containsFailCalls ? 'done' : ''; renameTestFunctionArgument(j, path, argumentName); } /** * Rewrite test callback to be able to destructure its argument * * test(({ok}) => {ok()}) to test(t => {ok()}) */ function rewriteDestructuredTArgument(fileInfo, j, ast, testFunctionName) { ast .find(j.CallExpression, { callee: function (callee) { return callee.name === testFunctionName || (callee.object && callee.object.name === testFunctionName); }, }) .forEach(function (p) { // The last arg is the test callback var lastArg = p.value.arguments[p.value.arguments.length - 1]; var lastArgParam = lastArg && lastArg.params && lastArg.params[0]; if (lastArgParam && lastArgParam.type === 'ObjectPattern') { var objectPattern = lastArg.params[0]; var keys = objectPattern.properties.map(function (prop) { return prop.key.name; }); lastArg.params[0] = j.identifier('t'); keys.forEach(function (key) { j(lastArg) .find(j.CallExpression, { callee: { name: key }, }) .forEach(function (assertion) { j(assertion).replaceWith(j.callExpression(j.memberExpression(j.identifier('t'), j.identifier(key)), assertion.node.arguments)); }); }); } }); } /** * Rewrite Execution reference name if not 't' * * @param fileInfo * @param {jscodeshift} j * @param {Collection} ast * @param {string} testFunctionName */ function renameExecutionInterface(fileInfo, j, ast, testFunctionName) { ast .find(j.CallExpression, { callee: function (callee) { return callee.name === testFunctionName || (callee.object && callee.object.name === testFunctionName); }, }) .forEach(function (p) { var _a; var lastArg = p.value.arguments[p.value.arguments.length - 1]; if ((_a = lastArg === null || lastArg === void 0 ? void 0 : lastArg.params) === null || _a === void 0 ? void 0 : _a[0]) { var lastArgName_1 = lastArg.params[0].name; if (lastArgName_1 === 't') { return; } j(p) .find(j.Identifier, { name: lastArgName_1, }) .filter(function (path) { return path.parent.node === lastArg; }) .forEach(function (path) { path.get('name').replace('t'); var rootScope = path.scope; j(p) .find(j.CallExpression, { callee: { object: { name: lastArgName_1 } } }) .forEach(function (path) { var scope = path.scope; while (scope && scope !== rootScope) { if (scope.declares(lastArgName_1)) { return; } scope = scope.parent; } path.node.callee.object.name = 't'; }); }); } }); } /** * Validated that "t" is the test argument name. * * Example: 'test(x => {})' gives a warning. */ function detectUnsupportedNaming(fileInfo, j, ast, testFunctionName) { ast .find(j.CallExpression, { callee: function (callee) { return callee.name === testFunctionName || (callee.object && callee.object.name === testFunctionName); }, }) .forEach(function (p) { var lastArg = p.value.arguments[p.value.arguments.length - 1]; if (lastArg && lastArg.params && lastArg.params[0]) { var lastArgName = lastArg.params[0].name; // Currently we only support "t" as the test argument name if (lastArgName !== 't') { (0, logger_1.default)(fileInfo, "Argument to test function should be named \"t\" not \"".concat(lastArgName, "\""), p); } } }); }