0.2.0 - Mid migration

This commit is contained in:
Daniel Mason 2022-04-25 14:47:15 +12:00
parent 139e6a915e
commit 7e38fdbd7d
42393 changed files with 5358157 additions and 62 deletions

View file

@ -0,0 +1,109 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RULE_NAME = void 0;
var experimental_utils_1 = require("@typescript-eslint/experimental-utils");
var utils_1 = require("../utils");
var node_utils_1 = require("../node-utils");
exports.RULE_NAME = 'await-async-query';
var ASYNC_QUERIES_REGEXP = /^find(All)?By(LabelText|PlaceholderText|Text|AltText|Title|DisplayValue|Role|TestId)$/;
function hasClosestExpectResolvesRejects(node) {
if (!node.parent) {
return false;
}
if (node_utils_1.isCallExpression(node) &&
node_utils_1.isIdentifier(node.callee) &&
node_utils_1.isMemberExpression(node.parent) &&
node.callee.name === 'expect') {
var expectMatcher = node.parent.property;
return (node_utils_1.isIdentifier(expectMatcher) &&
(expectMatcher.name === 'resolves' || expectMatcher.name === 'rejects'));
}
else {
return hasClosestExpectResolvesRejects(node.parent);
}
}
exports.default = experimental_utils_1.ESLintUtils.RuleCreator(utils_1.getDocsUrl)({
name: exports.RULE_NAME,
meta: {
type: 'problem',
docs: {
description: 'Enforce async queries to have proper `await`',
category: 'Best Practices',
recommended: 'warn',
},
messages: {
awaitAsyncQuery: '`{{ name }}` must have `await` operator',
},
fixable: null,
schema: [],
},
defaultOptions: [],
create: function (context) {
var _a;
var testingLibraryQueryUsage = [];
var isQueryUsage = function (node) {
return !node_utils_1.isAwaited(node.parent.parent) &&
!node_utils_1.isPromiseResolved(node) &&
!hasClosestExpectResolvesRejects(node);
};
var hasImportedFromTestingLibraryModule = false;
function report(params) {
if (hasImportedFromTestingLibraryModule) {
context.report(params);
}
}
return _a = {
'ImportDeclaration > ImportSpecifier,ImportNamespaceSpecifier': function (node) {
var importDeclaration = node.parent;
var module = importDeclaration.source.value.toString();
if (utils_1.LIBRARY_MODULES.includes(module)) {
hasImportedFromTestingLibraryModule = true;
}
}
},
_a["CallExpression > Identifier[name=" + ASYNC_QUERIES_REGEXP + "]"] = function (node) {
if (isQueryUsage(node)) {
testingLibraryQueryUsage.push({ node: node, queryName: node.name });
}
},
_a["MemberExpression > Identifier[name=" + ASYNC_QUERIES_REGEXP + "]"] = function (node) {
var parent = node.parent;
if (isQueryUsage(parent)) {
testingLibraryQueryUsage.push({ node: parent, queryName: node.name });
}
},
_a['Program:exit'] = function () {
testingLibraryQueryUsage.forEach(function (_a) {
var node = _a.node, queryName = _a.queryName;
var references = node_utils_1.getVariableReferences(context, node.parent.parent);
if (references && references.length === 0) {
report({
node: node,
messageId: 'awaitAsyncQuery',
data: {
name: queryName,
},
});
}
else {
for (var _i = 0, references_1 = references; _i < references_1.length; _i++) {
var reference = references_1[_i];
var referenceNode = reference.identifier;
if (!node_utils_1.isAwaited(referenceNode.parent) &&
!node_utils_1.isPromiseResolved(referenceNode)) {
report({
node: node,
messageId: 'awaitAsyncQuery',
data: {
name: queryName,
},
});
break;
}
}
}
});
},
_a;
},
});

View file

@ -0,0 +1,106 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RULE_NAME = void 0;
var experimental_utils_1 = require("@typescript-eslint/experimental-utils");
var utils_1 = require("../utils");
var node_utils_1 = require("../node-utils");
exports.RULE_NAME = 'await-async-utils';
var ASYNC_UTILS_REGEXP = new RegExp("^(" + utils_1.ASYNC_UTILS.join('|') + ")$");
function isPromiseAll(node) {
return node_utils_1.isMemberExpression(node.callee) && node_utils_1.isIdentifier(node.callee.object) && node.callee.object.name === 'Promise' && node_utils_1.isIdentifier(node.callee.property) && node.callee.property.name === 'all';
}
function isInPromiseAll(node) {
var parent = node.parent;
return node_utils_1.isCallExpression(parent) && node_utils_1.isArrayExpression(parent.parent) && node_utils_1.isCallExpression(parent.parent.parent) && isPromiseAll(parent.parent.parent);
}
exports.default = experimental_utils_1.ESLintUtils.RuleCreator(utils_1.getDocsUrl)({
name: exports.RULE_NAME,
meta: {
type: 'problem',
docs: {
description: 'Enforce async utils to be awaited properly',
category: 'Best Practices',
recommended: 'warn',
},
messages: {
awaitAsyncUtil: 'Promise returned from `{{ name }}` must be handled',
},
fixable: null,
schema: [],
},
defaultOptions: [],
create: function (context) {
var _a;
var asyncUtilsUsage = [];
var importedAsyncUtils = [];
return _a = {
'ImportDeclaration > ImportSpecifier,ImportNamespaceSpecifier': function (node) {
var parent = node.parent;
if (!utils_1.LIBRARY_MODULES.includes(parent.source.value.toString()))
return;
if (node_utils_1.isImportSpecifier(node)) {
importedAsyncUtils.push(node.imported.name);
}
if (node_utils_1.isImportNamespaceSpecifier(node)) {
importedAsyncUtils.push(node.local.name);
}
}
},
_a["CallExpression > Identifier[name=" + ASYNC_UTILS_REGEXP + "]"] = function (node) {
asyncUtilsUsage.push({ node: node, name: node.name });
},
_a["CallExpression > MemberExpression > Identifier[name=" + ASYNC_UTILS_REGEXP + "]"] = function (node) {
var memberExpression = node.parent;
var identifier = memberExpression.object;
var memberExpressionName = identifier.name;
asyncUtilsUsage.push({
node: memberExpression,
name: memberExpressionName,
});
},
_a['Program:exit'] = function () {
var testingLibraryUtilUsage = asyncUtilsUsage.filter(function (usage) {
if (node_utils_1.isMemberExpression(usage.node)) {
var object = usage.node.object;
return importedAsyncUtils.includes(object.name);
}
return importedAsyncUtils.includes(usage.name);
});
testingLibraryUtilUsage.forEach(function (_a) {
var node = _a.node, name = _a.name;
var references = node_utils_1.getVariableReferences(context, node.parent.parent);
if (references &&
references.length === 0 &&
!node_utils_1.isAwaited(node.parent.parent) &&
!node_utils_1.isPromiseResolved(node) &&
!isInPromiseAll(node)) {
context.report({
node: node,
messageId: 'awaitAsyncUtil',
data: {
name: name,
},
});
}
else {
for (var _i = 0, references_1 = references; _i < references_1.length; _i++) {
var reference = references_1[_i];
var referenceNode = reference.identifier;
if (!node_utils_1.isAwaited(referenceNode.parent) &&
!node_utils_1.isPromiseResolved(referenceNode)) {
context.report({
node: node,
messageId: 'awaitAsyncUtil',
data: {
name: name,
},
});
break;
}
}
}
});
},
_a;
},
});

View file

@ -0,0 +1,43 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RULE_NAME = void 0;
var experimental_utils_1 = require("@typescript-eslint/experimental-utils");
var utils_1 = require("../utils");
var node_utils_1 = require("../node-utils");
exports.RULE_NAME = 'await-fire-event';
exports.default = experimental_utils_1.ESLintUtils.RuleCreator(utils_1.getDocsUrl)({
name: exports.RULE_NAME,
meta: {
type: 'problem',
docs: {
description: 'Enforce async fire event methods to be awaited',
category: 'Best Practices',
recommended: false,
},
messages: {
awaitFireEvent: 'async `fireEvent.{{ methodName }}` must be awaited',
},
fixable: null,
schema: [],
},
defaultOptions: [],
create: function (context) {
return {
'CallExpression > MemberExpression > Identifier[name=fireEvent]': function (node) {
var memberExpression = node.parent;
var fireEventMethodNode = memberExpression.property;
if (node_utils_1.isIdentifier(fireEventMethodNode) &&
!node_utils_1.isAwaited(node.parent.parent.parent) &&
!node_utils_1.isPromiseResolved(fireEventMethodNode.parent)) {
context.report({
node: fireEventMethodNode,
messageId: 'awaitFireEvent',
data: {
methodName: fireEventMethodNode.name,
},
});
}
},
};
},
});

View file

@ -0,0 +1,105 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RULE_NAME = void 0;
var utils_1 = require("../utils");
var experimental_utils_1 = require("@typescript-eslint/experimental-utils");
var node_utils_1 = require("../node-utils");
exports.RULE_NAME = 'consistent-data-testid';
var FILENAME_PLACEHOLDER = '{fileName}';
exports.default = experimental_utils_1.ESLintUtils.RuleCreator(utils_1.getDocsUrl)({
name: exports.RULE_NAME,
meta: {
type: 'suggestion',
docs: {
description: 'Ensures consistent usage of `data-testid`',
category: 'Best Practices',
recommended: false,
},
messages: {
invalidTestId: '`{{attr}}` "{{value}}" should match `{{regex}}`',
},
fixable: null,
schema: [
{
type: 'object',
default: {},
additionalProperties: false,
required: ['testIdPattern'],
properties: {
testIdPattern: {
type: 'string',
},
testIdAttribute: {
default: 'data-testid',
oneOf: [
{
type: 'string',
},
{
type: 'array',
items: {
type: 'string',
},
},
],
},
},
},
],
},
defaultOptions: [
{
testIdPattern: '',
testIdAttribute: 'data-testid',
},
],
create: function (context, _a) {
var _b;
var options = _a[0];
var getFilename = context.getFilename;
var testIdPattern = options.testIdPattern, attr = options.testIdAttribute;
function getFileNameData() {
var splitPath = getFilename().split('/');
var fileNameWithExtension = splitPath.pop();
var parent = splitPath.pop();
var fileName = fileNameWithExtension.split('.').shift();
return {
fileName: fileName === 'index' ? parent : fileName,
};
}
function getTestIdValidator(fileName) {
return new RegExp(testIdPattern.replace(FILENAME_PLACEHOLDER, fileName));
}
function isTestIdAttribute(name) {
if (typeof attr === 'string') {
return attr === name;
}
else {
return attr.includes(name);
}
}
return _b = {},
_b["JSXIdentifier"] = function (node) {
if (!node_utils_1.isJSXAttribute(node.parent) ||
!node_utils_1.isLiteral(node.parent.value) ||
!isTestIdAttribute(node.name)) {
return;
}
var value = node.parent.value.value;
var fileName = getFileNameData().fileName;
var regex = getTestIdValidator(fileName);
if (value && typeof value === 'string' && !regex.test(value)) {
context.report({
node: node,
messageId: 'invalidTestId',
data: {
attr: node.name,
value: value,
regex: regex,
},
});
}
},
_b;
},
});

View file

@ -0,0 +1,51 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RULE_NAME = void 0;
var experimental_utils_1 = require("@typescript-eslint/experimental-utils");
var utils_1 = require("../utils");
var node_utils_1 = require("../node-utils");
exports.RULE_NAME = 'no-await-sync-events';
var SYNC_EVENTS_REGEXP = new RegExp("^(" + utils_1.SYNC_EVENTS.join('|') + ")$");
exports.default = experimental_utils_1.ESLintUtils.RuleCreator(utils_1.getDocsUrl)({
name: exports.RULE_NAME,
meta: {
type: 'problem',
docs: {
description: 'Disallow unnecessary `await` for sync events',
category: 'Best Practices',
recommended: 'error',
},
messages: {
noAwaitSyncEvents: '`{{ name }}` does not need `await` operator',
},
fixable: null,
schema: [],
},
defaultOptions: [],
create: function (context) {
var _a;
return _a = {},
_a["AwaitExpression > CallExpression > MemberExpression > Identifier[name=" + SYNC_EVENTS_REGEXP + "]"] = function (node) {
var memberExpression = node.parent;
var methodNode = memberExpression.property;
var callExpression = memberExpression.parent;
var lastArg = callExpression.arguments[callExpression.arguments.length - 1];
var withDelay = node_utils_1.isObjectExpression(lastArg) &&
lastArg.properties.some(function (property) {
return node_utils_1.isProperty(property) &&
node_utils_1.isIdentifier(property.key) &&
property.key.name === 'delay';
});
if (!(node.name === 'userEvent' && ['type', 'keyboard'].includes(methodNode.name) && withDelay)) {
context.report({
node: methodNode,
messageId: 'noAwaitSyncEvents',
data: {
name: node.name + "." + methodNode.name,
},
});
}
},
_a;
},
});

View file

@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RULE_NAME = void 0;
var experimental_utils_1 = require("@typescript-eslint/experimental-utils");
var utils_1 = require("../utils");
exports.RULE_NAME = 'no-await-sync-query';
var SYNC_QUERIES_REGEXP = /^(get|query)(All)?By(LabelText|PlaceholderText|Text|AltText|Title|DisplayValue|Role|TestId)$/;
exports.default = experimental_utils_1.ESLintUtils.RuleCreator(utils_1.getDocsUrl)({
name: exports.RULE_NAME,
meta: {
type: 'problem',
docs: {
description: 'Disallow unnecessary `await` for sync queries',
category: 'Best Practices',
recommended: 'error',
},
messages: {
noAwaitSyncQuery: '`{{ name }}` does not need `await` operator',
},
fixable: null,
schema: [],
},
defaultOptions: [],
create: function (context) {
var _a;
var reportError = function (node) {
return context.report({
node: node,
messageId: 'noAwaitSyncQuery',
data: {
name: node.name,
},
});
};
return _a = {},
_a["AwaitExpression > CallExpression > Identifier[name=" + SYNC_QUERIES_REGEXP + "]"] = reportError,
_a["AwaitExpression > CallExpression > MemberExpression > Identifier[name=" + SYNC_QUERIES_REGEXP + "]"] = reportError,
_a;
},
});

View file

@ -0,0 +1,159 @@
"use strict";
var __spreadArray = (this && this.__spreadArray) || function (to, from) {
for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
to[j] = from[i];
return to;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RULE_NAME = void 0;
var experimental_utils_1 = require("@typescript-eslint/experimental-utils");
var utils_1 = require("../utils");
var node_utils_1 = require("../node-utils");
exports.RULE_NAME = 'no-debug';
function isRenderVariableDeclarator(node, renderFunctions) {
if (node.init) {
if (node_utils_1.isAwaitExpression(node.init)) {
return (node.init.argument &&
node_utils_1.isRenderFunction(node.init.argument, __spreadArray([
'render'
], renderFunctions)));
}
else {
return (node_utils_1.isCallExpression(node.init) &&
node_utils_1.isRenderFunction(node.init, __spreadArray(['render'], renderFunctions)));
}
}
return false;
}
function hasTestingLibraryImportModule(importDeclarationNode) {
var literal = importDeclarationNode.source;
return utils_1.LIBRARY_MODULES.some(function (module) { return module === literal.value; });
}
exports.default = experimental_utils_1.ESLintUtils.RuleCreator(utils_1.getDocsUrl)({
name: exports.RULE_NAME,
meta: {
type: 'problem',
docs: {
description: 'Disallow unnecessary debug usages in the tests',
category: 'Best Practices',
recommended: 'warn',
},
messages: {
noDebug: 'Unexpected debug statement',
},
fixable: null,
schema: [
{
type: 'object',
properties: {
renderFunctions: {
type: 'array',
},
},
},
],
},
defaultOptions: [
{
renderFunctions: [],
},
],
create: function (context, _a) {
var _b;
var options = _a[0];
var hasDestructuredDebugStatement = false;
var renderVariableDeclarators = [];
var renderFunctions = options.renderFunctions;
var hasImportedScreen = false;
var wildcardImportName = null;
return _b = {
VariableDeclarator: function (node) {
if (isRenderVariableDeclarator(node, renderFunctions)) {
if (node_utils_1.isObjectPattern(node.id) &&
node.id.properties.some(function (property) {
return node_utils_1.isProperty(property) &&
node_utils_1.isIdentifier(property.key) &&
property.key.name === 'debug';
})) {
hasDestructuredDebugStatement = true;
}
if (node.id.type === 'Identifier') {
renderVariableDeclarators.push(node);
}
}
}
},
_b["VariableDeclarator > CallExpression > Identifier[name=\"require\"]"] = function (node) {
var args = node.parent.arguments;
var literalNodeScreenModuleName = args.find(function (args) {
return node_utils_1.isLiteral(args) &&
typeof args.value === 'string' &&
utils_1.LIBRARY_MODULES.includes(args.value);
});
if (!literalNodeScreenModuleName) {
return;
}
var declaratorNode = node.parent
.parent;
hasImportedScreen =
node_utils_1.isObjectPattern(declaratorNode.id) &&
declaratorNode.id.properties.some(function (property) {
return node_utils_1.isProperty(property) &&
node_utils_1.isIdentifier(property.key) &&
property.key.name === 'screen';
});
},
_b.ImportDeclaration = function (node) {
if (!hasTestingLibraryImportModule(node))
return;
hasImportedScreen = node.specifiers.some(function (s) { return node_utils_1.isImportSpecifier(s) && s.imported.name === 'screen'; });
},
_b['ImportDeclaration ImportNamespaceSpecifier'] = function (node) {
var importDeclarationNode = node.parent;
if (!hasTestingLibraryImportModule(importDeclarationNode))
return;
wildcardImportName = node.local && node.local.name;
},
_b["CallExpression > Identifier[name=\"debug\"]"] = function (node) {
if (hasDestructuredDebugStatement) {
context.report({
node: node,
messageId: 'noDebug',
});
}
},
_b["CallExpression > MemberExpression > Identifier[name=\"debug\"]"] = function (node) {
var memberExpression = node.parent;
var identifier = memberExpression.object;
var memberExpressionName = identifier.name;
var isScreenDebugUsed = hasImportedScreen && memberExpressionName === 'screen';
var isNamespaceDebugUsed = wildcardImportName && memberExpressionName === wildcardImportName;
if (isScreenDebugUsed || isNamespaceDebugUsed) {
context.report({
node: node,
messageId: 'noDebug',
});
}
},
_b['Program:exit'] = function () {
renderVariableDeclarators.forEach(function (renderVar) {
var renderVarReferences = context
.getDeclaredVariables(renderVar)[0]
.references.slice(1);
renderVarReferences.forEach(function (ref) {
var parent = ref.identifier.parent;
if (node_utils_1.isMemberExpression(parent) &&
node_utils_1.isIdentifier(parent.property) &&
parent.property.name === 'debug' &&
node_utils_1.isCallExpression(parent.parent)) {
context.report({
node: parent.property,
messageId: 'noDebug',
});
}
});
});
},
_b;
},
});

View file

@ -0,0 +1,90 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RULE_NAME = void 0;
var experimental_utils_1 = require("@typescript-eslint/experimental-utils");
var utils_1 = require("../utils");
var node_utils_1 = require("../node-utils");
exports.RULE_NAME = 'no-dom-import';
var DOM_TESTING_LIBRARY_MODULES = [
'dom-testing-library',
'@testing-library/dom',
];
exports.default = experimental_utils_1.ESLintUtils.RuleCreator(utils_1.getDocsUrl)({
name: exports.RULE_NAME,
meta: {
type: 'problem',
docs: {
description: 'Disallow importing from DOM Testing Library',
category: 'Best Practices',
recommended: false,
},
messages: {
noDomImport: 'import from DOM Testing Library is restricted, import from corresponding Testing Library framework instead',
noDomImportFramework: 'import from DOM Testing Library is restricted, import from {{module}} instead',
},
fixable: 'code',
schema: [
{
type: 'string',
},
],
},
defaultOptions: [''],
create: function (context, _a) {
var _b;
var framework = _a[0];
function report(node, moduleName) {
if (framework) {
var isRequire_1 = node_utils_1.isIdentifier(node) && node.name === 'require';
var correctModuleName_1 = moduleName.replace('dom', framework);
context.report({
node: node,
messageId: 'noDomImportFramework',
data: {
module: correctModuleName_1,
},
fix: function (fixer) {
if (isRequire_1) {
var callExpression = node.parent;
var name_1 = callExpression.arguments[0];
return fixer.replaceText(name_1, name_1.raw.replace(moduleName, correctModuleName_1));
}
else {
var importDeclaration = node;
var name_2 = importDeclaration.source;
return fixer.replaceText(name_2, name_2.raw.replace(moduleName, correctModuleName_1));
}
},
});
}
else {
context.report({
node: node,
messageId: 'noDomImport',
});
}
}
return _b = {
ImportDeclaration: function (node) {
var value = node.source.value;
var domModuleName = DOM_TESTING_LIBRARY_MODULES.find(function (module) { return module === value; });
if (domModuleName) {
report(node, domModuleName);
}
}
},
_b["CallExpression > Identifier[name=\"require\"]"] = function (node) {
var callExpression = node.parent;
var args = callExpression.arguments;
var literalNodeDomModuleName = args.find(function (args) {
return node_utils_1.isLiteral(args) &&
typeof args.value === 'string' &&
DOM_TESTING_LIBRARY_MODULES.includes(args.value);
});
if (literalNodeDomModuleName) {
report(node, literalNodeDomModuleName.value);
}
},
_b;
},
});

View file

@ -0,0 +1,110 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RULE_NAME = void 0;
var experimental_utils_1 = require("@typescript-eslint/experimental-utils");
var utils_1 = require("../utils");
var node_utils_1 = require("../node-utils");
exports.RULE_NAME = 'no-manual-cleanup';
var CLEANUP_LIBRARY_REGEX = /(@testing-library\/(preact|react|svelte|vue))|@marko\/testing-library/;
exports.default = experimental_utils_1.ESLintUtils.RuleCreator(utils_1.getDocsUrl)({
name: exports.RULE_NAME,
meta: {
type: 'problem',
docs: {
description: 'Disallow the use of `cleanup`',
category: 'Best Practices',
recommended: false,
},
messages: {
noManualCleanup: "`cleanup` is performed automatically by your test runner, you don't need manual cleanups.",
},
fixable: null,
schema: [],
},
defaultOptions: [],
create: function (context) {
var _a;
var defaultImportFromTestingLibrary;
var defaultRequireFromTestingLibrary;
function reportImportReferences(references) {
if (references && references.length > 0) {
references.forEach(function (reference) {
var utilsUsage = reference.identifier.parent;
if (node_utils_1.isMemberExpression(utilsUsage) &&
node_utils_1.isIdentifier(utilsUsage.property) &&
utilsUsage.property.name === 'cleanup') {
context.report({
node: utilsUsage.property,
messageId: 'noManualCleanup',
});
}
});
}
}
return _a = {
ImportDeclaration: function (node) {
var value = node.source.value;
var testingLibraryWithCleanup = value.match(CLEANUP_LIBRARY_REGEX);
if (!testingLibraryWithCleanup) {
return;
}
if (node_utils_1.isImportDefaultSpecifier(node.specifiers[0])) {
defaultImportFromTestingLibrary = node;
}
var cleanupSpecifier = node.specifiers.find(function (specifier) {
return node_utils_1.isImportSpecifier(specifier) &&
specifier.imported &&
specifier.imported.name === 'cleanup';
});
if (cleanupSpecifier) {
context.report({
node: cleanupSpecifier,
messageId: 'noManualCleanup',
});
}
}
},
_a["VariableDeclarator > CallExpression > Identifier[name=\"require\"]"] = function (node) {
var args = node.parent.arguments;
var literalNodeCleanupModuleName = args.find(function (args) {
return node_utils_1.isLiteral(args) &&
typeof args.value === 'string' &&
args.value.match(CLEANUP_LIBRARY_REGEX);
});
if (!literalNodeCleanupModuleName) {
return;
}
var declaratorNode = node.parent
.parent;
if (node_utils_1.isObjectPattern(declaratorNode.id)) {
var cleanupProperty = declaratorNode.id.properties.find(function (property) {
return node_utils_1.isProperty(property) &&
node_utils_1.isIdentifier(property.key) &&
property.key.name === 'cleanup';
});
if (cleanupProperty) {
context.report({
node: cleanupProperty,
messageId: 'noManualCleanup',
});
}
}
else {
defaultRequireFromTestingLibrary = declaratorNode.id;
}
},
_a['Program:exit'] = function () {
if (defaultImportFromTestingLibrary) {
var references = context.getDeclaredVariables(defaultImportFromTestingLibrary)[0].references;
reportImportReferences(references);
}
if (defaultRequireFromTestingLibrary) {
var references = context
.getDeclaredVariables(defaultRequireFromTestingLibrary.parent)[0]
.references.slice(1);
reportImportReferences(references);
}
},
_a;
},
});

View file

@ -0,0 +1,120 @@
"use strict";
var __spreadArray = (this && this.__spreadArray) || function (to, from) {
for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
to[j] = from[i];
return to;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.findClosestBeforeHook = exports.RULE_NAME = void 0;
var experimental_utils_1 = require("@typescript-eslint/experimental-utils");
var utils_1 = require("../utils");
var node_utils_1 = require("../node-utils");
exports.RULE_NAME = 'no-render-in-setup';
function findClosestBeforeHook(node, testingFrameworkSetupHooksToFilter) {
if (node === null)
return null;
if (node_utils_1.isCallExpression(node) &&
node_utils_1.isIdentifier(node.callee) &&
testingFrameworkSetupHooksToFilter.includes(node.callee.name)) {
return node.callee;
}
return findClosestBeforeHook(node.parent, testingFrameworkSetupHooksToFilter);
}
exports.findClosestBeforeHook = findClosestBeforeHook;
exports.default = experimental_utils_1.ESLintUtils.RuleCreator(utils_1.getDocsUrl)({
name: exports.RULE_NAME,
meta: {
type: 'problem',
docs: {
description: 'Disallow the use of `render` in setup functions',
category: 'Best Practices',
recommended: false,
},
messages: {
noRenderInSetup: 'Move `render` out of `{{name}}` and into individual tests.',
},
fixable: null,
schema: [
{
type: 'object',
properties: {
renderFunctions: {
type: 'array',
},
allowTestingFrameworkSetupHook: {
enum: utils_1.TESTING_FRAMEWORK_SETUP_HOOKS,
},
},
anyOf: [
{
required: ['renderFunctions'],
},
{
required: ['allowTestingFrameworkSetupHook'],
},
],
},
],
},
defaultOptions: [
{
renderFunctions: [],
allowTestingFrameworkSetupHook: '',
},
],
create: function (context, _a) {
var _b;
var _c = _a[0], renderFunctions = _c.renderFunctions, allowTestingFrameworkSetupHook = _c.allowTestingFrameworkSetupHook;
var renderImportedFromTestingLib = false;
var wildcardImportName = null;
return _b = {
'ImportDeclaration[source.value=/testing-library/] ImportNamespaceSpecifier': function (node) {
wildcardImportName = node.local && node.local.name;
},
'ImportDeclaration[source.value=/testing-library/]': function (node) {
renderImportedFromTestingLib = node.specifiers.some(function (specifier) {
return (node_utils_1.isImportSpecifier(specifier) && specifier.local.name === 'render');
});
}
},
_b["VariableDeclarator > CallExpression > Identifier[name=\"require\"]"] = function (node) {
var callExpressionArgs = node.parent.arguments;
var testingLibImport = callExpressionArgs.find(function (args) {
return node_utils_1.isLiteral(args) &&
typeof args.value === 'string' &&
RegExp(/testing-library/, 'g').test(args.value);
});
if (!testingLibImport) {
return;
}
var declaratorNode = node.parent
.parent;
renderImportedFromTestingLib =
node_utils_1.isObjectPattern(declaratorNode.id) &&
declaratorNode.id.properties.some(function (property) {
return node_utils_1.isProperty(property) &&
node_utils_1.isIdentifier(property.key) &&
property.key.name === 'render';
});
},
_b.CallExpression = function (node) {
var testingFrameworkSetupHooksToFilter = utils_1.TESTING_FRAMEWORK_SETUP_HOOKS;
if (allowTestingFrameworkSetupHook.length !== 0) {
testingFrameworkSetupHooksToFilter = utils_1.TESTING_FRAMEWORK_SETUP_HOOKS.filter(function (hook) { return hook !== allowTestingFrameworkSetupHook; });
}
var beforeHook = findClosestBeforeHook(node, testingFrameworkSetupHooksToFilter);
var disallowedRenderFns = renderImportedFromTestingLib || wildcardImportName
? __spreadArray(['render'], renderFunctions) : renderFunctions;
if (node_utils_1.isRenderFunction(node, disallowedRenderFns) && beforeHook) {
context.report({
node: node,
messageId: 'noRenderInSetup',
data: {
name: beforeHook.name,
},
});
}
},
_b;
},
});

View file

@ -0,0 +1,55 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RULE_NAME = void 0;
var experimental_utils_1 = require("@typescript-eslint/experimental-utils");
var utils_1 = require("../utils");
var node_utils_1 = require("../node-utils");
exports.RULE_NAME = 'no-wait-for-empty-callback';
var WAIT_EXPRESSION_QUERY = 'CallExpression[callee.name=/^(waitFor|waitForElementToBeRemoved)$/]';
exports.default = experimental_utils_1.ESLintUtils.RuleCreator(utils_1.getDocsUrl)({
name: exports.RULE_NAME,
meta: {
type: 'suggestion',
docs: {
description: "It's preferred to avoid empty callbacks in `waitFor` and `waitForElementToBeRemoved`",
category: 'Best Practices',
recommended: false,
},
messages: {
noWaitForEmptyCallback: 'Avoid passing empty callback to `{{ methodName }}`. Insert an assertion instead.',
},
fixable: null,
schema: [],
},
defaultOptions: [],
create: function (context) {
var _a;
function reportIfEmpty(node) {
if (node_utils_1.isBlockStatement(node.body) &&
node.body.body.length === 0 &&
node_utils_1.isCallExpression(node.parent) &&
node_utils_1.isIdentifier(node.parent.callee)) {
context.report({
node: node,
loc: node.body.loc.start,
messageId: 'noWaitForEmptyCallback',
data: {
methodName: node.parent.callee.name,
},
});
}
}
function reportNoop(node) {
context.report({
node: node,
loc: node.loc.start,
messageId: 'noWaitForEmptyCallback',
});
}
return _a = {},
_a[WAIT_EXPRESSION_QUERY + " > ArrowFunctionExpression"] = reportIfEmpty,
_a[WAIT_EXPRESSION_QUERY + " > FunctionExpression"] = reportIfEmpty,
_a[WAIT_EXPRESSION_QUERY + " > Identifier[name=\"noop\"]"] = reportNoop,
_a;
},
});

View file

@ -0,0 +1,101 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RULE_NAME = void 0;
var experimental_utils_1 = require("@typescript-eslint/experimental-utils");
var utils_1 = require("../utils");
var node_utils_1 = require("../node-utils");
exports.RULE_NAME = 'no-wait-for-snapshot';
var ASYNC_UTILS_REGEXP = new RegExp("^(" + utils_1.ASYNC_UTILS.join('|') + ")$");
var SNAPSHOT_REGEXP = /^(toMatchSnapshot|toMatchInlineSnapshot)$/;
exports.default = experimental_utils_1.ESLintUtils.RuleCreator(utils_1.getDocsUrl)({
name: exports.RULE_NAME,
meta: {
type: 'problem',
docs: {
description: 'Ensures no snapshot is generated inside of a `waitFor` call',
category: 'Best Practices',
recommended: 'warn',
},
messages: {
noWaitForSnapshot: "A snapshot can't be generated inside of a `{{ name }}` call",
},
fixable: null,
schema: [],
},
defaultOptions: [],
create: function (context) {
var _a;
var asyncUtilsUsage = [];
var importedAsyncUtils = [];
var snapshotUsage = [];
return _a = {
'ImportDeclaration > ImportSpecifier,ImportNamespaceSpecifier': function (node) {
var parent = node.parent;
if (!utils_1.LIBRARY_MODULES.includes(parent.source.value.toString()))
return;
var name;
if (node.type === 'ImportSpecifier') {
name = node.imported.name;
}
if (node.type === 'ImportNamespaceSpecifier') {
name = node.local.name;
}
importedAsyncUtils.push(name);
}
},
_a["CallExpression > Identifier[name=" + ASYNC_UTILS_REGEXP + "]"] = function (node) {
asyncUtilsUsage.push({ node: node, name: node.name });
},
_a["CallExpression > MemberExpression > Identifier[name=" + ASYNC_UTILS_REGEXP + "]"] = function (node) {
var memberExpression = node.parent;
var identifier = memberExpression.object;
var memberExpressionName = identifier.name;
asyncUtilsUsage.push({
node: memberExpression,
name: memberExpressionName,
});
},
_a["Identifier[name=" + SNAPSHOT_REGEXP + "]"] = function (node) {
snapshotUsage.push(node);
},
_a['Program:exit'] = function () {
var testingLibraryUtilUsage = asyncUtilsUsage.filter(function (usage) {
if (node_utils_1.isMemberExpression(usage.node)) {
var object = usage.node.object;
return importedAsyncUtils.includes(object.name);
}
return importedAsyncUtils.includes(usage.name);
});
function getClosestAsyncUtil(asyncUtilUsage, node) {
var callExpression = node_utils_1.findClosestCallExpressionNode(node);
while (callExpression != null) {
if (callExpression.callee === asyncUtilUsage.node)
return asyncUtilUsage;
callExpression = node_utils_1.findClosestCallExpressionNode(callExpression.parent);
}
return null;
}
snapshotUsage.forEach(function (node) {
testingLibraryUtilUsage.forEach(function (asyncUtilUsage) {
var closestAsyncUtil = getClosestAsyncUtil(asyncUtilUsage, node);
if (closestAsyncUtil != null) {
var name_1;
if (node_utils_1.isMemberExpression(closestAsyncUtil.node)) {
name_1 = closestAsyncUtil.node.property
.name;
}
else {
name_1 = closestAsyncUtil.name;
}
context.report({
node: node,
messageId: 'noWaitForSnapshot',
data: { name: name_1 },
});
}
});
});
},
_a;
},
});

View file

@ -0,0 +1,102 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RULE_NAME = void 0;
var experimental_utils_1 = require("@typescript-eslint/experimental-utils");
var utils_1 = require("../utils");
var node_utils_1 = require("../node-utils");
exports.RULE_NAME = 'prefer-explicit-assert';
var ALL_GET_BY_QUERIES = utils_1.ALL_QUERIES_METHODS.map(function (queryMethod) { return "get" + queryMethod; });
var isValidQuery = function (node, customQueryNames) {
return ALL_GET_BY_QUERIES.includes(node.name) ||
customQueryNames.includes(node.name);
};
var isAtTopLevel = function (node) {
return node.parent.parent.type === 'ExpressionStatement';
};
exports.default = experimental_utils_1.ESLintUtils.RuleCreator(utils_1.getDocsUrl)({
name: exports.RULE_NAME,
meta: {
type: 'suggestion',
docs: {
description: 'Suggest using explicit assertions rather than just `getBy*` queries',
category: 'Best Practices',
recommended: false,
},
messages: {
preferExplicitAssert: 'Wrap stand-alone `getBy*` query with `expect` function for better explicit assertion',
preferExplicitAssertAssertion: '`getBy*` queries must be asserted with `{{assertion}}`',
},
fixable: null,
schema: [
{
type: 'object',
additionalProperties: false,
properties: {
assertion: {
type: 'string',
enum: utils_1.PRESENCE_MATCHERS,
},
customQueryNames: {
type: 'array',
},
},
},
],
},
defaultOptions: [
{
customQueryNames: [],
},
],
create: function (context, _a) {
var options = _a[0];
var customQueryNames = options.customQueryNames, assertion = options.assertion;
var getQueryCalls = [];
return {
'CallExpression Identifier': function (node) {
if (isValidQuery(node, customQueryNames)) {
getQueryCalls.push(node);
}
},
'Program:exit': function () {
getQueryCalls.forEach(function (queryCall) {
var node = node_utils_1.isMemberExpression(queryCall.parent)
? queryCall.parent
: queryCall;
if (isAtTopLevel(node)) {
context.report({
node: queryCall,
messageId: 'preferExplicitAssert',
});
}
else if (assertion) {
var expectCallNode = node_utils_1.findClosestCallNode(node, 'expect');
if (!expectCallNode)
return;
var expectStatement = expectCallNode.parent;
var property = expectStatement.property;
var matcher = property.name;
var isNegatedMatcher = false;
if (matcher === 'not' &&
node_utils_1.isMemberExpression(expectStatement.parent) &&
node_utils_1.isIdentifier(expectStatement.parent.property)) {
isNegatedMatcher = true;
matcher = expectStatement.parent.property.name;
}
var shouldEnforceAssertion = (!isNegatedMatcher && utils_1.PRESENCE_MATCHERS.includes(matcher)) ||
(isNegatedMatcher && utils_1.ABSENCE_MATCHERS.includes(matcher));
if (shouldEnforceAssertion && matcher !== assertion) {
context.report({
node: property,
messageId: 'preferExplicitAssertAssertion',
data: {
assertion: assertion,
},
});
}
}
});
},
};
},
});

View file

@ -0,0 +1,134 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFindByQueryVariant = exports.WAIT_METHODS = exports.RULE_NAME = void 0;
var experimental_utils_1 = require("@typescript-eslint/experimental-utils");
var node_utils_1 = require("../node-utils");
var utils_1 = require("../utils");
exports.RULE_NAME = 'prefer-find-by';
exports.WAIT_METHODS = ['waitFor', 'waitForElement', 'wait'];
function getFindByQueryVariant(queryMethod) {
return queryMethod.includes('All') ? 'findAllBy' : 'findBy';
}
exports.getFindByQueryVariant = getFindByQueryVariant;
function findRenderDefinitionDeclaration(scope, query) {
if (!scope) {
return null;
}
var variable = scope.variables.find(function (v) { return v.name === query; });
if (variable) {
var def = variable.defs.find(function (_a) {
var name = _a.name;
return name.name === query;
});
return def.name;
}
return findRenderDefinitionDeclaration(scope.upper, query);
}
exports.default = experimental_utils_1.ESLintUtils.RuleCreator(utils_1.getDocsUrl)({
name: exports.RULE_NAME,
meta: {
type: 'suggestion',
docs: {
description: 'Suggest using find* instead of waitFor to wait for elements',
category: 'Best Practices',
recommended: 'warn',
},
messages: {
preferFindBy: 'Prefer {{queryVariant}}{{queryMethod}} method over using await {{fullQuery}}',
},
fixable: 'code',
schema: [],
},
defaultOptions: [],
create: function (context) {
var sourceCode = context.getSourceCode();
function reportInvalidUsage(node, _a) {
var queryVariant = _a.queryVariant, queryMethod = _a.queryMethod, fix = _a.fix;
context.report({
node: node,
messageId: 'preferFindBy',
data: {
queryVariant: queryVariant,
queryMethod: queryMethod,
fullQuery: sourceCode.getText(node),
},
fix: fix,
});
}
return {
'AwaitExpression > CallExpression': function (node) {
if (!node_utils_1.isIdentifier(node.callee) ||
!exports.WAIT_METHODS.includes(node.callee.name)) {
return;
}
var argument = node.arguments[0];
if (!node_utils_1.isArrowFunctionExpression(argument)) {
return;
}
if (!node_utils_1.isCallExpression(argument.body)) {
return;
}
if (node_utils_1.isMemberExpression(argument.body.callee) &&
node_utils_1.isIdentifier(argument.body.callee.property) &&
node_utils_1.isIdentifier(argument.body.callee.object) &&
utils_1.SYNC_QUERIES_COMBINATIONS.includes(argument.body.callee.property.name)) {
var fullQueryMethod = argument.body.callee.property.name;
var caller_1 = argument.body.callee.object.name;
var queryVariant_1 = getFindByQueryVariant(fullQueryMethod);
var callArguments_1 = argument.body.arguments;
var queryMethod_1 = fullQueryMethod.split('By')[1];
reportInvalidUsage(node, {
queryMethod: queryMethod_1,
queryVariant: queryVariant_1,
fix: function (fixer) {
var newCode = caller_1 + "." + queryVariant_1 + queryMethod_1 + "(" + callArguments_1
.map(function (node) { return sourceCode.getText(node); })
.join(', ') + ")";
return fixer.replaceText(node, newCode);
},
});
return;
}
if (node_utils_1.isIdentifier(argument.body.callee) &&
utils_1.SYNC_QUERIES_COMBINATIONS.includes(argument.body.callee.name)) {
var fullQueryMethod_1 = argument.body.callee.name;
var queryMethod_2 = fullQueryMethod_1.split('By')[1];
var queryVariant_2 = getFindByQueryVariant(fullQueryMethod_1);
var callArguments_2 = argument.body.arguments;
reportInvalidUsage(node, {
queryMethod: queryMethod_2,
queryVariant: queryVariant_2,
fix: function (fixer) {
var findByMethod = "" + queryVariant_2 + queryMethod_2;
var allFixes = [];
var newCode = findByMethod + "(" + callArguments_2
.map(function (node) { return sourceCode.getText(node); })
.join(', ') + ")";
allFixes.push(fixer.replaceText(node, newCode));
var definition = findRenderDefinitionDeclaration(context.getScope(), fullQueryMethod_1);
if (!definition) {
return allFixes;
}
if (node_utils_1.isObjectPattern(definition.parent.parent)) {
var allVariableDeclarations = definition.parent.parent;
if (allVariableDeclarations.properties.some(function (p) {
return node_utils_1.isProperty(p) &&
node_utils_1.isIdentifier(p.key) &&
p.key.name === findByMethod;
})) {
return allFixes;
}
var textDestructuring = sourceCode.getText(allVariableDeclarations);
var text = textDestructuring.substring(0, textDestructuring.length - 2) +
(", " + findByMethod + " }");
allFixes.push(fixer.replaceText(allVariableDeclarations, text));
}
return allFixes;
},
});
return;
}
},
};
},
});

View file

@ -0,0 +1,66 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RULE_NAME = void 0;
var experimental_utils_1 = require("@typescript-eslint/experimental-utils");
var utils_1 = require("../utils");
var node_utils_1 = require("../node-utils");
exports.RULE_NAME = 'prefer-presence-queries';
var QUERIES_REGEXP = new RegExp("^(get|query)(All)?(" + utils_1.ALL_QUERIES_METHODS.join('|') + ")$");
function isThrowingQuery(node) {
return node.name.startsWith('get');
}
exports.default = experimental_utils_1.ESLintUtils.RuleCreator(utils_1.getDocsUrl)({
name: exports.RULE_NAME,
meta: {
docs: {
category: 'Best Practices',
description: 'Ensure appropriate get*/query* queries are used with their respective matchers',
recommended: 'error',
},
messages: {
presenceQuery: 'Use `getBy*` queries rather than `queryBy*` for checking element is present',
absenceQuery: 'Use `queryBy*` queries rather than `getBy*` for checking element is NOT present',
expectQueryBy: 'Use `getBy*` only when checking elements are present, otherwise use `queryBy*`',
},
schema: [],
type: 'suggestion',
fixable: null,
},
defaultOptions: [],
create: function (context) {
var _a;
return _a = {},
_a["CallExpression Identifier[name=" + QUERIES_REGEXP + "]"] = function (node) {
var expectCallNode = node_utils_1.findClosestCallNode(node, 'expect');
if (expectCallNode && node_utils_1.isMemberExpression(expectCallNode.parent)) {
var expectStatement = expectCallNode.parent;
var property = expectStatement.property;
var matcher = property.name;
var isNegatedMatcher = false;
if (matcher === 'not' &&
node_utils_1.isMemberExpression(expectStatement.parent) &&
node_utils_1.isIdentifier(expectStatement.parent.property)) {
isNegatedMatcher = true;
matcher = expectStatement.parent.property.name;
}
var validMatchers = isThrowingQuery(node)
? utils_1.PRESENCE_MATCHERS
: utils_1.ABSENCE_MATCHERS;
var invalidMatchers = isThrowingQuery(node)
? utils_1.ABSENCE_MATCHERS
: utils_1.PRESENCE_MATCHERS;
var messageId = isThrowingQuery(node)
? 'absenceQuery'
: 'presenceQuery';
if ((!isNegatedMatcher && invalidMatchers.includes(matcher)) ||
(isNegatedMatcher && validMatchers.includes(matcher))) {
return context.report({
node: node,
messageId: messageId,
});
}
}
},
_a;
},
});

View file

@ -0,0 +1,113 @@
"use strict";
var __spreadArray = (this && this.__spreadArray) || function (to, from) {
for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
to[j] = from[i];
return to;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RULE_NAME = void 0;
var experimental_utils_1 = require("@typescript-eslint/experimental-utils");
var utils_1 = require("../utils");
var node_utils_1 = require("../node-utils");
exports.RULE_NAME = 'prefer-screen-queries';
var ALLOWED_RENDER_PROPERTIES_FOR_DESTRUCTURING = [
'container',
'baseElement',
];
var ALL_QUERIES_COMBINATIONS_REGEXP = utils_1.ALL_QUERIES_COMBINATIONS.join('|');
function usesContainerOrBaseElement(node) {
var secondArgument = node.arguments[1];
return (node_utils_1.isObjectExpression(secondArgument) &&
secondArgument.properties.some(function (property) {
return node_utils_1.isProperty(property) &&
node_utils_1.isIdentifier(property.key) &&
ALLOWED_RENDER_PROPERTIES_FOR_DESTRUCTURING.includes(property.key.name);
}));
}
exports.default = experimental_utils_1.ESLintUtils.RuleCreator(utils_1.getDocsUrl)({
name: exports.RULE_NAME,
meta: {
type: 'suggestion',
docs: {
description: 'Suggest using screen while using queries',
category: 'Best Practices',
recommended: false,
},
messages: {
preferScreenQueries: 'Use screen to query DOM elements, `screen.{{ name }}`',
},
fixable: null,
schema: [],
},
defaultOptions: [],
create: function (context) {
var _a;
function reportInvalidUsage(node) {
context.report({
node: node,
messageId: 'preferScreenQueries',
data: {
name: node.name,
},
});
}
var queriesRegex = new RegExp(ALL_QUERIES_COMBINATIONS_REGEXP);
var queriesDestructuredInWithinDeclaration = [];
var withinDeclaredVariables = [];
return _a = {
VariableDeclarator: function (node) {
if (!node_utils_1.isCallExpression(node.init) || !node_utils_1.isIdentifier(node.init.callee)) {
return;
}
var isWithinFunction = node.init.callee.name === 'within';
var usesRenderOptions = node.init.callee.name === 'render' &&
usesContainerOrBaseElement(node.init);
if (!isWithinFunction && !usesRenderOptions) {
return;
}
if (node_utils_1.isObjectPattern(node.id)) {
var identifiers = node.id.properties
.filter(function (property) {
return node_utils_1.isProperty(property) &&
node_utils_1.isIdentifier(property.key) &&
queriesRegex.test(property.key.name);
})
.map(function (property) {
return property.key.name;
});
queriesDestructuredInWithinDeclaration.push.apply(queriesDestructuredInWithinDeclaration, identifiers);
return;
}
if (node_utils_1.isIdentifier(node.id)) {
withinDeclaredVariables.push(node.id.name);
}
}
},
_a["CallExpression > Identifier[name=/^" + ALL_QUERIES_COMBINATIONS_REGEXP + "$/]"] = function (node) {
if (!queriesDestructuredInWithinDeclaration.some(function (queryName) { return queryName === node.name; })) {
reportInvalidUsage(node);
}
},
_a["MemberExpression > Identifier[name=/^" + ALL_QUERIES_COMBINATIONS_REGEXP + "$/]"] = function (node) {
function isIdentifierAllowed(name) {
return __spreadArray(['screen'], withinDeclaredVariables).includes(name);
}
if (node_utils_1.isIdentifier(node) &&
node_utils_1.isMemberExpression(node.parent) &&
node_utils_1.isCallExpression(node.parent.object) &&
node_utils_1.isIdentifier(node.parent.object.callee) &&
node.parent.object.callee.name !== 'within' &&
node.parent.object.callee.name === 'render' &&
!usesContainerOrBaseElement(node.parent.object)) {
reportInvalidUsage(node);
return;
}
if (node_utils_1.isMemberExpression(node.parent) &&
node_utils_1.isIdentifier(node.parent.object) &&
!isIdentifierAllowed(node.parent.object.name)) {
reportInvalidUsage(node);
}
},
_a;
},
});

View file

@ -0,0 +1,113 @@
"use strict";
var __spreadArray = (this && this.__spreadArray) || function (to, from) {
for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
to[j] = from[i];
return to;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RULE_NAME = void 0;
var experimental_utils_1 = require("@typescript-eslint/experimental-utils");
var utils_1 = require("../utils");
var node_utils_1 = require("../node-utils");
exports.RULE_NAME = 'prefer-wait-for';
var DEPRECATED_METHODS = ['wait', 'waitForElement', 'waitForDomChange'];
exports.default = experimental_utils_1.ESLintUtils.RuleCreator(utils_1.getDocsUrl)({
name: exports.RULE_NAME,
meta: {
type: 'suggestion',
docs: {
description: 'Use `waitFor` instead of deprecated wait methods',
category: 'Best Practices',
recommended: false,
},
messages: {
preferWaitForMethod: '`{{ methodName }}` is deprecated in favour of `waitFor`',
preferWaitForImport: 'import `waitFor` instead of deprecated async utils',
},
fixable: 'code',
schema: [],
},
defaultOptions: [],
create: function (context) {
var reportImport = function (node) {
context.report({
node: node,
messageId: 'preferWaitForImport',
fix: function (fixer) {
var excludedImports = __spreadArray(__spreadArray([], DEPRECATED_METHODS), ['waitFor']);
var newImports = node.specifiers
.filter(function (specifier) {
return node_utils_1.isImportSpecifier(specifier) &&
!excludedImports.includes(specifier.imported.name);
})
.map(function (specifier) { return specifier.imported.name; });
newImports.push('waitFor');
var newNode = "import { " + newImports.join(',') + " } from '" + node.source.value + "';";
return fixer.replaceText(node, newNode);
},
});
};
var reportWait = function (node) {
context.report({
node: node,
messageId: 'preferWaitForMethod',
data: {
methodName: node.name,
},
fix: function (fixer) {
var callExpressionNode = node_utils_1.findClosestCallExpressionNode(node);
var arg = callExpressionNode.arguments[0];
var fixers = [];
if (arg) {
fixers.push(fixer.replaceText(node, 'waitFor'));
if (node.name === 'waitForDomChange') {
fixers.push(fixer.insertTextBefore(arg, '() => {}, '));
}
}
else {
var methodReplacement = 'waitFor(() => {})';
if (node_utils_1.isMemberExpression(node.parent) &&
node_utils_1.isIdentifier(node.parent.object)) {
methodReplacement = node.parent.object.name + "." + methodReplacement;
}
var newText = methodReplacement;
fixers.push(fixer.replaceText(callExpressionNode, newText));
}
return fixers;
},
});
};
return {
'ImportDeclaration[source.value=/testing-library/]': function (node) {
var deprecatedImportSpecifiers = node.specifiers.filter(function (specifier) {
return node_utils_1.isImportSpecifier(specifier) &&
specifier.imported &&
DEPRECATED_METHODS.includes(specifier.imported.name);
});
deprecatedImportSpecifiers.forEach(function (importSpecifier, i) {
if (i === 0) {
reportImport(node);
}
context
.getDeclaredVariables(importSpecifier)
.forEach(function (variable) {
return variable.references.forEach(function (reference) {
return reportWait(reference.identifier);
});
});
});
},
'ImportDeclaration[source.value=/testing-library/] > ImportNamespaceSpecifier': function (node) {
context.getDeclaredVariables(node).forEach(function (variable) {
return variable.references.forEach(function (reference) {
if (node_utils_1.isMemberExpression(reference.identifier.parent) &&
node_utils_1.isIdentifier(reference.identifier.parent.property) &&
DEPRECATED_METHODS.includes(reference.identifier.parent.property.name)) {
reportWait(reference.identifier.parent.property);
}
});
});
},
};
},
});