jest-codemods
Version:
Codemods for migrating test files to Jest
228 lines (227 loc) • 8 kB
JavaScript
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);
}
}
});
}
;