Skip to content

Instantly share code, notes, and snippets.

@krainboltgreene
Created September 6, 2019 04:13
Show Gist options
  • Save krainboltgreene/694be83f3d6573a1cfc16786d511d6a9 to your computer and use it in GitHub Desktop.
Save krainboltgreene/694be83f3d6573a1cfc16786d511d6a9 to your computer and use it in GitHub Desktop.
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = tapeToJest;
var _imports = require("../utils/imports");
var _consts = require("../utils/consts");
var _tapeAvaHelpers = require("../utils/tape-ava-helpers");
var _logger = _interopRequireDefault(require("../utils/logger"));
var _finale = _interopRequireDefault(require("../utils/finale"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Codemod for transforming Tape tests into Jest.
*/
var SPECIAL_THROWS_CASE = '(special throws case)';
var SPECIAL_PLAN_CASE = '(special plan case)';
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,
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.removeRequireAndImport)(j, ast, 'tap');
if (!testFunctionName) {
// No Tape require/import were found
if (!options.skipImportDetection) {
return fileInfo.source;
}
testFunctionName = 'tap';
}
var logWarning = function logWarning(msg, node) {
return (0, _logger.default)(fileInfo, msg, node);
};
var transforms = [function () {
return (0, _tapeAvaHelpers.rewriteDestructuredTArgument)(fileInfo, j, ast, testFunctionName);
}, function () {
return (0, _tapeAvaHelpers.detectUnsupportedNaming)(fileInfo, j, ast, testFunctionName);
}, function detectUnsupportedFeatures() {
ast.find(j.CallExpression, {
callee: {
object: {
name: 't'
},
property: function property(_ref) {
var name = _ref.name;
return tPropertiesUnsupported.has(name);
}
}
}).forEach(function (p) {
var propertyName = p.value.callee.property.name;
if (propertyName.toLowerCase().indexOf('looseequal') >= 0) {
logWarning(`"t.${propertyName}" is currently not supported. Try the stricter "toEqual" or "not.toEqual"`, p);
} else {
logWarning(`"t.${propertyName}" is currently not supported`, p);
}
});
ast.find(j.CallExpression, {
callee: {
object: {
name: testFunctionName
},
property: function property(_ref2) {
var name = _ref2.name;
return unsupportedTestFunctionProperties.has(name);
}
}
}).forEach(function (p) {
var propertyName = p.value.callee.property.name;
logWarning(`"${propertyName}" is currently not supported`, p);
});
}, function updateAssertions() {
ast.find(j.CallExpression, {
callee: {
object: {
name: 't'
},
property: function property(_ref3) {
var name = _ref3.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.${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_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.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 === 'timeout') {
logWarning('"timeout" option is currently not supported', p);
}
});
p.value.arguments = p.value.arguments.filter(function (pa) {
return pa.type !== 'ObjectExpression';
});
}
});
if (p.node.callee.name !== 'test.skip') {
p.node.callee.name = 'test';
}
(0, _tapeAvaHelpers.rewriteAssertionsAndTestArgument)(j, p);
});
}];
transforms.forEach(function (t) {
return t();
});
return (0, _finale.default)(fileInfo, j, ast, options);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment