jest-codemods
Version:
Codemods for migrating test files to Jest
185 lines (182 loc) • 7.13 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = expectJsTransfomer;
/*
This a codeshift for 'expect.js' to Jest's expect
https://github.com/Automattic/expect.js
No support yet for the follow expect.js conventions:
expect({ a: 'b', c: 'd' }).to.only.have.keys(['a', 'c']);
*/
var finale_1 = __importDefault(require("../utils/finale"));
var imports_1 = require("../utils/imports");
var logger_1 = __importDefault(require("../utils/logger"));
var MATCHES = {
be: 'toBe',
equal: 'toBe',
eql: 'toEqual',
contain: 'toContain',
length: 'toHaveLength',
property: 'toHaveProperty',
key: 'toHaveProperty',
above: 'toBeGreaterThan',
greaterThan: 'toBeGreaterThan',
gt: 'toBeGreaterThan',
below: 'toBeLessThan',
lessThan: 'toBeLessThan',
lt: 'toBeLessThan',
match: 'toMatch',
};
var NOT_SUPPORTED = ['keys'];
var SPECIAL_MATCHES = [
'ok',
'fail',
'a',
'an',
'empty',
'throwError',
'throw',
'throwException',
'within',
];
var TYPE_OF_MATCHES = [
'function',
'string',
'object',
'number',
'undefined',
'boolean',
'symbol',
];
var MATCHER_METHODS = Object.keys(MATCHES).concat(SPECIAL_MATCHES).concat(NOT_SUPPORTED);
var EXPECT_JS = 'expect.js';
function expectJsTransfomer(fileInfo, api, options) {
var j = api.jscodeshift;
var ast = j(fileInfo.source);
var expectImport = (0, imports_1.getRequireOrImportName)(j, ast, EXPECT_JS);
var logWarning = function (msg, node) { return (0, logger_1.default)(fileInfo, msg, node); };
if (!expectImport && !options.skipImportDetection) {
// No expect.js require/import were found
return fileInfo.source;
}
(0, imports_1.removeRequireAndImport)(j, ast, EXPECT_JS);
var t = makeTransformApi(j);
// transform expect.js assertion syntax
ast
.find(j.ExpressionStatement, {
expression: {
type: 'CallExpression',
callee: {
type: 'MemberExpression',
property: function (node) {
return node.type === 'Identifier' && MATCHER_METHODS.indexOf(node.name) !== -1;
},
},
},
})
.replaceWith(function (path) {
var callExpression = path.value.expression;
var expectCall = getExpectCallExpression(callExpression);
if (!expectCall) {
return path.node;
}
var negation = hasNegation(callExpression);
var matcherArg = callExpression.arguments[0];
var name = callExpression.callee.property.name;
switch (name) {
case 'ok':
return t.transform(negation ? 'toBeFalsy' : 'toBeTruthy', expectCall);
case 'fail':
return j.throwStatement(j.callExpression(j.identifier('Error'), matcherArg ? [matcherArg] : []));
case 'a':
case 'an':
return t.makeInstanceOfExpect(expectCall, negation, matcherArg);
case 'empty':
return t.transform('toHaveLength', expectCall, negation, j.identifier('0'));
case 'throwError':
case 'throw':
case 'throwException':
return t.makeThrowExpect(expectCall, negation, matcherArg);
case 'within':
return t.makeWithinExpect(expectCall, negation, matcherArg, callExpression.arguments[1]);
case 'keys':
logWarning('Unsupported Expect.js Assertion "*.keys"', path);
return path.node;
default:
return t.transform(MATCHES[name], expectCall, negation, matcherArg);
}
});
return (0, finale_1.default)(fileInfo, j, ast, options, expectImport);
}
function makeTransformApi(j) {
function makeNewExpect(expectArg) {
return j.callExpression(j.identifier('expect'), [expectArg]);
}
function transform(matcher, expectCall, negation, expectation) {
if (negation === void 0) { negation = false; }
if (expectation === void 0) { expectation = null; }
return j.expressionStatement(j.callExpression(j.memberExpression(expectCall, j.identifier((negation ? 'not.' : '') + matcher)), expectation ? [expectation] : []));
}
function makeInstanceOfExpect(expectCall, negation, expectation) {
if (expectation.value) {
if (TYPE_OF_MATCHES.indexOf(expectation.value) !== -1) {
var expectCallArg = expectCall.arguments[0];
return transform('toBe', makeNewExpect(j.unaryExpression('typeof', expectCallArg)), negation, expectation);
}
else if (expectation.value === 'array') {
return transform('toBeInstanceOf', expectCall, negation, j.identifier('Array'));
}
}
return transform('toBeInstanceOf', expectCall, negation, expectation);
}
function makeWithinExpect(expectCall, negation, arg1, arg2) {
var expectArg = expectCall.arguments[0];
return transform('toBeTruthy', makeNewExpect(j.logicalExpression('&&', j.binaryExpression('>', expectArg, arg1), j.binaryExpression('<', expectArg, arg2))));
}
function makeThrowExpect(expectCall, negation, matcherArg) {
if (matcherArg && matcherArg.type === 'FunctionExpression') {
var expectFn = expectCall.arguments[0].type === 'Identifier'
? j.identifier(expectCall.arguments[0].name)
: expectCall.arguments[0];
return j.tryStatement(j.blockStatement([
j.expressionStatement(j.callExpression(expectFn, [])),
j.throwStatement(j.callExpression(j.identifier('Error'), [j.literal("Function did not throw")])),
]), j.catchClause(matcherArg.params[0], null, j.blockStatement(matcherArg.body.body)));
}
var matcher = matcherArg ? 'toThrowError' : 'toThrow';
return transform(matcher, expectCall, negation, matcherArg);
}
return {
makeNewExpect: makeNewExpect,
makeInstanceOfExpect: makeInstanceOfExpect,
makeWithinExpect: makeWithinExpect,
makeThrowExpect: makeThrowExpect,
transform: transform,
};
}
function getExpectCallExpression(node) {
if (node.type === 'CallExpression') {
if (node.callee.type === 'Identifier' && node.callee.name === 'expect') {
return node;
}
return getExpectCallExpression(node.callee);
}
else if (node.type === 'MemberExpression') {
return getExpectCallExpression(node.object);
}
return false;
}
function hasNegation(node) {
if (node.type === 'CallExpression') {
return hasNegation(node.callee);
}
else if (node.type === 'MemberExpression') {
if (node.property.type === 'Identifier' && node.property.name === 'not') {
return true;
}
return hasNegation(node.object);
}
return false;
}