eslint-plugin-sonarjs
Version:
85 lines (84 loc) • 4.31 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.TESTING_LIBRARY_MODULES = void 0;
exports.isDirectTestingLibraryBinding = isDirectTestingLibraryBinding;
exports.importsTestingLibrary = importsTestingLibrary;
exports.withStrictImportResolution = withStrictImportResolution;
const ast_js_1 = require("./ast.js");
const module_js_1 = require("./module.js");
exports.TESTING_LIBRARY_MODULES = [
'@testing-library/dom',
'@testing-library/react',
'@testing-library/vue',
'@testing-library/angular',
'@testing-library/svelte',
];
function isDirectTestingLibraryBinding(context, node, importedName, options) {
if ((0, ast_js_1.isIdentifier)(node)) {
return isDirectNamedImport(context, node, importedName, options.allowSubpathImport);
}
return (options.allowNamespaceImport &&
node.type === 'MemberExpression' &&
!node.computed &&
(0, ast_js_1.isIdentifier)(node.object) &&
(0, ast_js_1.isIdentifier)(node.property, importedName) &&
isDirectNamespaceImport(context, node.object, options.allowSubpathImport));
}
function isDirectNamedImport(context, node, importedName, allowSubpathImport) {
const variable = (0, ast_js_1.getVariableFromName)(context, node.name, node);
const definition = variable?.defs[0];
return (variable?.defs.length === 1 &&
definition?.type === 'ImportBinding' &&
definition.node.type === 'ImportSpecifier' &&
(0, ast_js_1.isIdentifier)(definition.node.imported, importedName) &&
definition.parent.type === 'ImportDeclaration' &&
typeof definition.parent.source.value === 'string' &&
isTestingLibraryModule(definition.parent.source.value, allowSubpathImport));
}
function isDirectNamespaceImport(context, node, allowSubpathImport) {
const variable = (0, ast_js_1.getVariableFromName)(context, node.name, node);
const definition = variable?.defs[0];
return (variable?.defs.length === 1 &&
definition?.type === 'ImportBinding' &&
definition.node.type === 'ImportNamespaceSpecifier' &&
definition.parent.type === 'ImportDeclaration' &&
typeof definition.parent.source.value === 'string' &&
isTestingLibraryModule(definition.parent.source.value, allowSubpathImport));
}
function isTestingLibraryModule(moduleName, allowSubpathImport) {
return exports.TESTING_LIBRARY_MODULES.some((module) => moduleName === module || (allowSubpathImport && moduleName.startsWith(`${module}/`)));
}
function importsTestingLibrary(context) {
return [...(0, module_js_1.getCurrentFileImports)(context.sourceCode)].some(moduleName => isTestingLibraryModule(moduleName, true));
}
// Without any `testing-library/*` settings, the upstream `eslint-plugin-testing-library`
// rules fall back to "aggressive" name-based heuristics for `waitFor`, `fireEvent`,
// `userEvent`, `render`, and custom queries: they treat any call merely *named* like one
// of these as a Testing Library util, regardless of where it's imported from. Forcing
// these settings (the upstream plugin's own documented recipe for disabling aggressive
// reporting) switches detection to import resolution instead. The official
// `@testing-library/*` module check still runs unconditionally, so real Testing Library
// imports are unaffected. Note this does not make *built-in* query detection
// (`getBy*`/`queryBy*`/`findBy*`) import-resolution-based: upstream matches those purely
// by name in all modes, so a same-named, unrelated helper (e.g. Playwright's
// `page.getByRole`) can still be misidentified as a Testing Library query. Accepted as a
// known v1 limitation inherited from the upstream plugin.
const STRICT_IMPORT_RESOLUTION_SETTINGS = {
'testing-library/utils-module': 'off',
'testing-library/custom-renders': 'off',
'testing-library/custom-queries': 'off',
};
function withStrictImportResolution(rule) {
return {
...rule,
create(context) {
const overriddenContext = Object.create(context, {
settings: {
value: { ...context.settings, ...STRICT_IMPORT_RESOLUTION_SETTINGS },
enumerable: true,
},
});
return rule.create(overriddenContext);
},
};
}