jest-codemods
Version:
Codemods for migrating test files to Jest
227 lines (226 loc) • 9.66 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = tapeToJest;
/**
* Codemod for transforming Tape tests into Jest.
*/
var consts_1 = require("../utils/consts");
var finale_1 = __importDefault(require("../utils/finale"));
var imports_1 = require("../utils/imports");
var logger_1 = __importDefault(require("../utils/logger"));
var tape_ava_helpers_1 = require("../utils/tape-ava-helpers");
var SPECIAL_THROWS_CASE = '(special throws case)';
var SPECIAL_REJECTS_CASE = '(special rejects case)';
var SPECIAL_PLAN_CASE = '(special plan case)';
var SUPPORTED_TEST_METHODS = ['test.skip', 'test.todo'];
var tPropertiesMap = {
ok: 'toBeTruthy',
true: 'toBeTruthy',
assert: 'toBeTruthy',
notOk: 'toBeFalsy',
false: 'toBeFalsy',
notok: 'toBeFalsy',
error: 'toBeFalsy',
ifError: 'toBeFalsy',
ifErr: 'toBeFalsy',
iferror: 'toBeFalsy',
equal: 'toBe',
equals: 'toBe',
isEqual: 'toBe',
is: 'toBe',
strictEqual: 'toBe',
strictEquals: 'toBe',
notEqual: 'not.toBe',
notEquals: 'not.toBe',
notStrictEqual: 'not.toBe',
notStrictEquals: 'not.toBe',
isNotEqual: 'not.toBe',
isNot: 'not.toBe',
not: 'not.toBe',
doesNotEqual: 'not.toBe',
isInequal: 'not.toBe',
deepEqual: 'toEqual',
deepEquals: 'toEqual',
isEquivalent: 'toEqual',
same: 'toEqual',
notDeepEqual: 'not.toEqual',
notEquivalent: 'not.toEqual',
notDeeply: 'not.toEqual',
notSame: 'not.toEqual',
isNotDeepEqual: 'not.toEqual',
isNotDeeply: 'not.toEqual',
isNotEquivalent: 'not.toEqual',
isInequivalent: 'not.toEqual',
throws: SPECIAL_THROWS_CASE,
rejects: SPECIAL_REJECTS_CASE,
doesNotThrow: SPECIAL_THROWS_CASE,
plan: SPECIAL_PLAN_CASE,
};
var tPropertiesNotMapped = new Set(['pass', 'fail', 'end', 'comment']);
var tPropertiesUnsupported = new Set([
'timeoutAfter', // toEqual is more strict but might be used in some cases:
'deepLooseEqual',
'looseEqual',
'looseEquals',
'notDeepLooseEqual',
'notLooseEqual',
'notLooseEquals',
'skip',
]);
var unsupportedTestFunctionProperties = new Set(['createStream', 'onFinish']);
function tapeToJest(fileInfo, api, options) {
var j = api.jscodeshift;
var ast = j(fileInfo.source);
var testFunctionName = (0, imports_1.removeRequireAndImport)(j, ast, 'tape');
if (!testFunctionName) {
// tape and tap have the same API
testFunctionName = (0, imports_1.removeRequireAndImport)(j, ast, 'tap');
}
if (!testFunctionName) {
// No Tape require/import were found
if (!options.skipImportDetection) {
return fileInfo.source;
}
testFunctionName = 'tape';
}
var logWarning = function (msg, node) { return (0, logger_1.default)(fileInfo, msg, node); };
var transforms = [
function () { return (0, tape_ava_helpers_1.rewriteDestructuredTArgument)(fileInfo, j, ast, testFunctionName); },
function () { return (0, tape_ava_helpers_1.detectUnsupportedNaming)(fileInfo, j, ast, testFunctionName); },
function detectUnsupportedFeatures() {
ast
.find(j.CallExpression, {
callee: {
object: { name: 't' },
property: function (_a) {
var name = _a.name;
return tPropertiesUnsupported.has(name);
},
},
})
.forEach(function (p) {
var propertyName = p.value.callee.property.name;
if (propertyName.toLowerCase().indexOf('looseequal') >= 0) {
logWarning("\"t.".concat(propertyName, "\" is currently not supported. Try the stricter \"toEqual\" or \"not.toEqual\""), p);
}
else {
logWarning("\"t.".concat(propertyName, "\" is currently not supported"), p);
}
});
ast
.find(j.CallExpression, {
callee: {
object: { name: testFunctionName },
property: function (_a) {
var name = _a.name;
return unsupportedTestFunctionProperties.has(name);
},
},
})
.forEach(function (p) {
var propertyName = p.value.callee.property.name;
logWarning("\"".concat(propertyName, "\" is currently not supported"), p);
});
},
function updateAssertions() {
ast
.find(j.CallExpression, {
callee: {
object: { name: 't' },
property: function (_a) {
var name = _a.name;
return !tPropertiesUnsupported.has(name) && !tPropertiesNotMapped.has(name);
},
},
})
.forEach(function (p) {
var args = p.node.arguments;
var oldPropertyName = p.value.callee.property.name;
var newPropertyName = tPropertiesMap[oldPropertyName];
if (typeof newPropertyName === 'undefined') {
logWarning("\"t.".concat(oldPropertyName, "\" is currently not supported"), p);
return null;
}
var newCondition;
if (newPropertyName === SPECIAL_THROWS_CASE) {
// The semantics of t.throws(fn, expected, msg) in Tape:
// If `expected` is a string, it is set to msg, else exception reg exp
var secondArgString = args.length === 2 &&
args[1].type === 'Literal' &&
typeof args[1].value === 'string';
var noErrorType = args.length === 1 || secondArgString;
if (noErrorType) {
newCondition = j.callExpression(j.identifier(oldPropertyName === 'throws' ? 'toThrow' : 'not.toThrow'), []);
}
else {
newCondition = j.callExpression(j.identifier(oldPropertyName === 'throws' ? 'toThrowError' : 'not.toThrowError'), [args[1]]);
}
}
else if (newPropertyName == SPECIAL_REJECTS_CASE) {
newCondition = j.callExpression(j.identifier('rejects.toStrictEqual'), [
args[1],
]);
}
else if (newPropertyName === SPECIAL_PLAN_CASE) {
var condition = j.memberExpression(j.identifier('expect'), j.callExpression(j.identifier('assertions'), [args[0]]));
return j(p).replaceWith(condition);
}
else {
var hasSecondArgument = consts_1.PROP_WITH_SECONDS_ARGS.indexOf(newPropertyName) >= 0;
var conditionArgs = hasSecondArgument ? [args[1]] : [];
newCondition = j.callExpression(j.identifier(newPropertyName), conditionArgs);
}
var newExpression = j.memberExpression(j.callExpression(j.identifier('expect'), [args[0]]), newCondition);
return j(p).replaceWith(newExpression);
});
},
function updateTapeComments() {
ast
.find(j.CallExpression, {
callee: {
object: { name: 't' },
property: { name: 'comment' },
},
})
.forEach(function (p) {
p.node.callee = 'console.log';
});
},
function rewriteTestCallExpression() {
ast
.find(j.CallExpression, {
callee: { name: testFunctionName },
})
.forEach(function (p) {
// Convert Tape option parameters, test([name], [opts], cb)
p.value.arguments.forEach(function (a) {
if (a.type === 'ObjectExpression') {
a.properties.forEach(function (tapeOption) {
var tapeOptionKey = tapeOption.key.name;
var tapeOptionValue = tapeOption.value.value;
if (tapeOptionKey === 'skip' && tapeOptionValue === true) {
p.value.callee.name = 'test.skip';
}
if (tapeOptionKey === 'todo' && tapeOptionValue === true) {
p.value.callee.name = 'test.todo';
}
if (tapeOptionKey === 'timeout') {
logWarning('"timeout" option is currently not supported', p);
}
});
p.value.arguments = p.value.arguments.filter(function (pa) { return pa.type !== 'ObjectExpression'; });
}
});
if (!SUPPORTED_TEST_METHODS.includes(p.node.callee.name)) {
p.node.callee.name = 'test';
}
(0, tape_ava_helpers_1.rewriteAssertionsAndTestArgument)(j, p);
});
},
];
transforms.forEach(function (t) { return t(); });
return (0, finale_1.default)(fileInfo, j, ast, options);
}
;