eslint-plugin-jest-formatting
Version:
ESLint rules for formatting jest tests
170 lines (169 loc) • 6.54 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.createRule = exports.PaddingType = exports.StatementType = void 0;
const astUtils = require("../ast-utils");
var StatementType;
(function (StatementType) {
StatementType[StatementType["Any"] = 0] = "Any";
StatementType[StatementType["AfterAllToken"] = 1] = "AfterAllToken";
StatementType[StatementType["AfterEachToken"] = 2] = "AfterEachToken";
StatementType[StatementType["BeforeAllToken"] = 3] = "BeforeAllToken";
StatementType[StatementType["BeforeEachToken"] = 4] = "BeforeEachToken";
StatementType[StatementType["DescribeToken"] = 5] = "DescribeToken";
StatementType[StatementType["ExpectToken"] = 6] = "ExpectToken";
StatementType[StatementType["FdescribeToken"] = 7] = "FdescribeToken";
StatementType[StatementType["FitToken"] = 8] = "FitToken";
StatementType[StatementType["ItToken"] = 9] = "ItToken";
StatementType[StatementType["TestToken"] = 10] = "TestToken";
StatementType[StatementType["XdescribeToken"] = 11] = "XdescribeToken";
StatementType[StatementType["XitToken"] = 12] = "XitToken";
StatementType[StatementType["XtestToken"] = 13] = "XtestToken";
})(StatementType = exports.StatementType || (exports.StatementType = {}));
var PaddingType;
(function (PaddingType) {
PaddingType[PaddingType["Any"] = 0] = "Any";
PaddingType[PaddingType["Always"] = 1] = "Always";
})(PaddingType = exports.PaddingType || (exports.PaddingType = {}));
const createTokenTester = (tokenName) => {
return (node, sourceCode) => {
let activeNode = node;
if (activeNode.type === 'ExpressionStatement') {
if (activeNode.expression.type === 'AwaitExpression') {
activeNode = activeNode.expression.argument;
}
const token = sourceCode.getFirstToken(activeNode);
return token.type === 'Identifier' && token.value === tokenName;
}
return false;
};
};
const statementTesters = {
[0]: () => true,
[1]: createTokenTester('afterAll'),
[2]: createTokenTester('afterEach'),
[3]: createTokenTester('beforeAll'),
[4]: createTokenTester('beforeEach'),
[5]: createTokenTester('describe'),
[6]: createTokenTester('expect'),
[7]: createTokenTester('fdescribe'),
[8]: createTokenTester('fit'),
[9]: createTokenTester('it'),
[10]: createTokenTester('test'),
[11]: createTokenTester('xdescribe'),
[12]: createTokenTester('xit'),
[13]: createTokenTester('xtest'),
};
const paddingAlwaysTester = (prevNode, nextNode, paddingContext) => {
const { sourceCode, ruleContext } = paddingContext;
const paddingLines = astUtils.getPaddingLineSequences(prevNode, nextNode, sourceCode);
if (paddingLines.length > 0) {
return;
}
ruleContext.report({
node: nextNode,
message: 'Expected blank line before this statement.',
fix(fixer) {
let prevToken = astUtils.getActualLastToken(sourceCode, prevNode);
const nextToken = (sourceCode.getFirstTokenBetween(prevToken, nextNode, {
includeComments: true,
filter(token) {
if (astUtils.areTokensOnSameLine(prevToken, token)) {
prevToken = token;
return false;
}
return true;
},
}) || nextNode);
const insertText = astUtils.areTokensOnSameLine(prevToken, nextToken)
? '\n\n'
: '\n';
return fixer.insertTextAfter(prevToken, insertText);
},
});
};
const paddingTesters = {
[0]: () => true,
[1]: paddingAlwaysTester,
};
const createScopeInfo = () => {
return (() => {
let scope = null;
return {
get prevNode() {
return scope.prevNode;
},
set prevNode(node) {
scope.prevNode = node;
},
enter() {
scope = { upper: scope, prevNode: null };
},
exit() {
scope = scope.upper;
},
};
})();
};
const nodeMatchesType = (node, statementType, paddingContext) => {
let innerStatementNode = node;
const { sourceCode } = paddingContext;
while (innerStatementNode.type === 'LabeledStatement') {
innerStatementNode = innerStatementNode.body;
}
if (Array.isArray(statementType)) {
return statementType.some((type) => nodeMatchesType(innerStatementNode, type, paddingContext));
}
return statementTesters[statementType](innerStatementNode, sourceCode);
};
const testPadding = (prevNode, nextNode, paddingContext) => {
const { configs } = paddingContext;
const testType = (type) => paddingTesters[type](prevNode, nextNode, paddingContext);
for (let i = configs.length - 1; i >= 0; --i) {
const { prevStatementType: prevType, nextStatementType: nextType, paddingType, } = configs[i];
if (nodeMatchesType(prevNode, prevType, paddingContext) &&
nodeMatchesType(nextNode, nextType, paddingContext)) {
return testType(paddingType);
}
}
return testType(0);
};
const verifyNode = (node, paddingContext) => {
const { scopeInfo } = paddingContext;
if (!astUtils.isValidParent(node.parent.type)) {
return;
}
if (scopeInfo.prevNode) {
testPadding(scopeInfo.prevNode, node, paddingContext);
}
scopeInfo.prevNode = node;
};
const createRule = (configs, deprecated = false) => ({
meta: {
fixable: 'whitespace',
deprecated,
},
create(context) {
const paddingContext = {
ruleContext: context,
sourceCode: context.getSourceCode(),
scopeInfo: createScopeInfo(),
configs,
};
const { scopeInfo } = paddingContext;
return {
Program: scopeInfo.enter,
'Program:exit': scopeInfo.enter,
BlockStatement: scopeInfo.enter,
'BlockStatement:exit': scopeInfo.exit,
SwitchStatement: scopeInfo.enter,
'SwitchStatement:exit': scopeInfo.exit,
':statement': (node) => verifyNode(node, paddingContext),
SwitchCase: (node) => {
verifyNode(node, paddingContext);
scopeInfo.enter();
},
'SwitchCase:exit': scopeInfo.exit,
};
},
});
exports.createRule = createRule;