diff --git a/lib/detect-testing-library-utils.ts b/lib/detect-testing-library-utils.ts index a5faa373..0c81fa6e 100644 --- a/lib/detect-testing-library-utils.ts +++ b/lib/detect-testing-library-utils.ts @@ -78,7 +78,7 @@ type IsRenderUtilFn = (node: TSESTree.Identifier) => boolean; type IsRenderVariableDeclaratorFn = ( node: TSESTree.VariableDeclarator ) => boolean; -type IsDebugUtilFn = (node: TSESTree.Identifier) => boolean; +type IsDebugUtilFn = (identifierNode: TSESTree.Identifier) => boolean; type IsPresenceAssertFn = (node: TSESTree.MemberExpression) => boolean; type IsAbsenceAssertFn = (node: TSESTree.MemberExpression) => boolean; type CanReportErrorsFn = () => boolean; @@ -580,14 +580,22 @@ export function detectTestingLibraryUtils< return isRenderUtil(initIdentifierNode); }; - const isDebugUtil: IsDebugUtilFn = (node) => { - return isTestingLibraryUtil( - node, - (identifierNodeName, originalNodeName) => { - return [identifierNodeName, originalNodeName] - .filter(Boolean) - .includes('debug'); - } + const isDebugUtil: IsDebugUtilFn = (identifierNode) => { + const isBuiltInConsole = + isMemberExpression(identifierNode.parent) && + ASTUtils.isIdentifier(identifierNode.parent.object) && + identifierNode.parent.object.name === 'console'; + + return ( + !isBuiltInConsole && + isTestingLibraryUtil( + identifierNode, + (identifierNodeName, originalNodeName) => { + return [identifierNodeName, originalNodeName] + .filter(Boolean) + .includes('debug'); + } + ) ); }; diff --git a/lib/rules/no-debug.ts b/lib/rules/no-debug.ts index 5e2222e5..71e0f8f1 100644 --- a/lib/rules/no-debug.ts +++ b/lib/rules/no-debug.ts @@ -4,6 +4,7 @@ import { getInnermostReturningFunction, getPropertyIdentifierNode, getReferenceNode, + isCallExpression, isObjectPattern, isProperty, } from '../node-utils'; @@ -34,6 +35,7 @@ export default createTestingLibraryRule({ const suspiciousDebugVariableNames: string[] = []; const suspiciousReferenceNodes: TSESTree.Identifier[] = []; const renderWrapperNames: string[] = []; + const builtInConsoleNodes: TSESTree.VariableDeclarator[] = []; function detectRenderWrapper(node: TSESTree.Identifier): void { const innerFunction = getInnermostReturningFunction(context, node); @@ -54,6 +56,11 @@ export default createTestingLibraryRule({ return; } + if (initIdentifierNode.name === 'console') { + builtInConsoleNodes.push(node); + return; + } + const isRenderWrapperVariableDeclarator = initIdentifierNode ? renderWrapperNames.includes(initIdentifierNode.name) : false; @@ -120,7 +127,21 @@ export default createTestingLibraryRule({ } ); - if (isDebugUtil || isDeclaredDebugVariable || isChainedReferenceDebug) { + const isVariableFromBuiltInConsole = builtInConsoleNodes.some( + (variableDeclarator) => { + const variables = context.getDeclaredVariables(variableDeclarator); + return variables.some( + ({ name }) => + name === callExpressionIdentifier.name && + isCallExpression(callExpressionIdentifier.parent) + ); + } + ); + + if ( + !isVariableFromBuiltInConsole && + (isDebugUtil || isDeclaredDebugVariable || isChainedReferenceDebug) + ) { context.report({ node: callExpressionIdentifier, messageId: 'noDebug', diff --git a/tests/lib/rules/no-debug.test.ts b/tests/lib/rules/no-debug.test.ts index fda5faab..7606b915 100644 --- a/tests/lib/rules/no-debug.test.ts +++ b/tests/lib/rules/no-debug.test.ts @@ -54,6 +54,27 @@ ruleTester.run(RULE_NAME, rule, { settings: { 'testing-library/utils-module': 'test-utils' }, code: `screen.debug()`, }, + { + code: `console.debug()`, + }, + { + code: ` + const consoleDebug = console.debug + consoleDebug() + `, + }, + { + code: ` + const { debug } = console + debug() + `, + }, + { + code: ` + const { debug: consoleDebug } = console + consoleDebug() + `, + }, { code: ` const { screen } = require('@testing-library/dom') @@ -516,5 +537,23 @@ ruleTester.run(RULE_NAME, rule, { `, errors: [{ line: 7, column: 7, messageId: 'noDebug' }], }, + { + settings: { 'testing-library/utils-module': 'test-utils' }, + code: ` + import { render } from '@testing-library/react' + + const utils = render(element) + const { debug: renamedDestructuredDebug } = console + const { debug } = console + const assignedDebug = console.debug + console.debug('debugging') + debug('destructured') + assignedDebug('foo') + // the following line is the one that fails + utils.debug() + renamedDestructuredDebug('foo') + `, + errors: [{ line: 12, column: 13, messageId: 'noDebug' }], + }, ], });