From 4e4d317ce3401fca2afa264585cfe0a56b92ced5 Mon Sep 17 00:00:00 2001 From: Gonzalo D'Elia Date: Fri, 11 Dec 2020 19:28:17 -0300 Subject: [PATCH 1/2] refactor: migrate no-wait-for-snapshot to v4 --- lib/rules/no-wait-for-snapshot.ts | 128 +++++-------------- tests/lib/rules/no-wait-for-snapshot.test.ts | 72 +++++++++-- 2 files changed, 97 insertions(+), 103 deletions(-) diff --git a/lib/rules/no-wait-for-snapshot.ts b/lib/rules/no-wait-for-snapshot.ts index 039b4cdd..f0fb4200 100644 --- a/lib/rules/no-wait-for-snapshot.ts +++ b/lib/rules/no-wait-for-snapshot.ts @@ -1,5 +1,6 @@ -import { ESLintUtils, TSESTree } from '@typescript-eslint/experimental-utils'; -import { getDocsUrl, ASYNC_UTILS, LIBRARY_MODULES } from '../utils'; +import { ASTUtils, TSESTree } from '@typescript-eslint/experimental-utils'; +import { createTestingLibraryRule } from '../create-testing-library-rule'; +import { ASYNC_UTILS } from '../utils'; import { findClosestCallExpressionNode, isMemberExpression, @@ -9,10 +10,9 @@ export const RULE_NAME = 'no-wait-for-snapshot'; export type MessageIds = 'noWaitForSnapshot'; type Options = []; -const ASYNC_UTILS_REGEXP = new RegExp(`^(${ASYNC_UTILS.join('|')})$`); const SNAPSHOT_REGEXP = /^(toMatchSnapshot|toMatchInlineSnapshot)$/; -export default ESLintUtils.RuleCreator(getDocsUrl)({ +export default createTestingLibraryRule({ name: RULE_NAME, meta: { type: 'problem', @@ -31,102 +31,40 @@ export default ESLintUtils.RuleCreator(getDocsUrl)({ }, defaultOptions: [], - create(context) { - const asyncUtilsUsage: Array<{ - node: TSESTree.Identifier | TSESTree.MemberExpression; - name: string; - }> = []; - const importedAsyncUtils: string[] = []; - const snapshotUsage: TSESTree.Identifier[] = []; - - return { - 'ImportDeclaration > ImportSpecifier,ImportNamespaceSpecifier'( - node: TSESTree.Node - ) { - const parent = node.parent as TSESTree.ImportDeclaration; - - if (!LIBRARY_MODULES.includes(parent.source.value.toString())) { - return; - } - - let name; - if (node.type === 'ImportSpecifier') { - name = node.imported.name; + create(context, _, helpers) { + function getClosestAsyncUtil(node: TSESTree.Node) { + let n = node; + do { + const callExpression = findClosestCallExpressionNode(n); + if ( + ASTUtils.isIdentifier(callExpression.callee) && + helpers.isNodeComingFromTestingLibrary(callExpression.callee) && + ASYNC_UTILS.includes(callExpression.callee.name) + ) { + return callExpression.callee; } - - if (node.type === 'ImportNamespaceSpecifier') { - name = node.local.name; + if ( + isMemberExpression(callExpression.callee) && + ASTUtils.isIdentifier(callExpression.callee.property) && + helpers.isNodeComingFromTestingLibrary(callExpression.callee) + ) { + return callExpression.callee.property; } + n = findClosestCallExpressionNode(callExpression.parent); + } while (n !== null); + return null; + } - importedAsyncUtils.push(name); - }, - [`CallExpression > Identifier[name=${ASYNC_UTILS_REGEXP}]`]( - node: TSESTree.Identifier - ) { - asyncUtilsUsage.push({ node, name: node.name }); - }, - [`CallExpression > MemberExpression > Identifier[name=${ASYNC_UTILS_REGEXP}]`]( - node: TSESTree.Identifier - ) { - const memberExpression = node.parent as TSESTree.MemberExpression; - const identifier = memberExpression.object as TSESTree.Identifier; - const memberExpressionName = identifier.name; - - asyncUtilsUsage.push({ - node: memberExpression, - name: memberExpressionName, - }); - }, + return { [`Identifier[name=${SNAPSHOT_REGEXP}]`](node: TSESTree.Identifier) { - snapshotUsage.push(node); - }, - 'Program:exit'() { - const testingLibraryUtilUsage = asyncUtilsUsage.filter((usage) => { - if (isMemberExpression(usage.node)) { - const object = usage.node.object as TSESTree.Identifier; - - return importedAsyncUtils.includes(object.name); - } - - return importedAsyncUtils.includes(usage.name); - }); - - function getClosestAsyncUtil( - asyncUtilUsage: { - node: TSESTree.Identifier | TSESTree.MemberExpression; - name: string; - }, - node: TSESTree.Node - ) { - let callExpression = findClosestCallExpressionNode(node); - while (callExpression != null) { - if (callExpression.callee === asyncUtilUsage.node) - return asyncUtilUsage; - callExpression = findClosestCallExpressionNode( - callExpression.parent - ); - } - return null; + const closestAsyncUtil = getClosestAsyncUtil(node); + if (closestAsyncUtil === null) { + return; } - - snapshotUsage.forEach((node) => { - testingLibraryUtilUsage.forEach((asyncUtilUsage) => { - const closestAsyncUtil = getClosestAsyncUtil(asyncUtilUsage, node); - if (closestAsyncUtil != null) { - let name; - if (isMemberExpression(closestAsyncUtil.node)) { - name = (closestAsyncUtil.node.property as TSESTree.Identifier) - .name; - } else { - name = closestAsyncUtil.name; - } - context.report({ - node, - messageId: 'noWaitForSnapshot', - data: { name }, - }); - } - }); + context.report({ + node, + messageId: 'noWaitForSnapshot', + data: { name: closestAsyncUtil.name }, }); }, }; diff --git a/tests/lib/rules/no-wait-for-snapshot.test.ts b/tests/lib/rules/no-wait-for-snapshot.test.ts index 522bf12d..70e44c03 100644 --- a/tests/lib/rules/no-wait-for-snapshot.test.ts +++ b/tests/lib/rules/no-wait-for-snapshot.test.ts @@ -131,7 +131,14 @@ ruleTester.run(RULE_NAME, rule, { await ${asyncUtil}(() => expect(foo).toMatchSnapshot()); }); `, - errors: [{ line: 4, messageId: 'noWaitForSnapshot' }], + errors: [ + { + line: 4, + messageId: 'noWaitForSnapshot', + data: { name: asyncUtil }, + column: 36 + asyncUtil.length, + }, + ], })), ...ASYNC_UTILS.map((asyncUtil) => ({ code: ` @@ -142,7 +149,14 @@ ruleTester.run(RULE_NAME, rule, { }); }); `, - errors: [{ line: 5, messageId: 'noWaitForSnapshot' }], + errors: [ + { + line: 5, + messageId: 'noWaitForSnapshot', + data: { name: asyncUtil }, + column: 27, + }, + ], })), ...ASYNC_UTILS.map((asyncUtil) => ({ code: ` @@ -151,7 +165,14 @@ ruleTester.run(RULE_NAME, rule, { await asyncUtils.${asyncUtil}(() => expect(foo).toMatchSnapshot()); }); `, - errors: [{ line: 4, messageId: 'noWaitForSnapshot' }], + errors: [ + { + line: 4, + messageId: 'noWaitForSnapshot', + data: { name: asyncUtil }, + column: 47 + asyncUtil.length, + }, + ], })), ...ASYNC_UTILS.map((asyncUtil) => ({ code: ` @@ -162,7 +183,14 @@ ruleTester.run(RULE_NAME, rule, { }); }); `, - errors: [{ line: 5, messageId: 'noWaitForSnapshot' }], + errors: [ + { + line: 5, + messageId: 'noWaitForSnapshot', + data: { name: asyncUtil }, + column: 27, + }, + ], })), ...ASYNC_UTILS.map((asyncUtil) => ({ code: ` @@ -171,7 +199,14 @@ ruleTester.run(RULE_NAME, rule, { await ${asyncUtil}(() => expect(foo).toMatchInlineSnapshot()); }); `, - errors: [{ line: 4, messageId: 'noWaitForSnapshot' }], + errors: [ + { + line: 4, + messageId: 'noWaitForSnapshot', + data: { name: asyncUtil }, + column: 36 + asyncUtil.length, + }, + ], })), ...ASYNC_UTILS.map((asyncUtil) => ({ code: ` @@ -182,7 +217,14 @@ ruleTester.run(RULE_NAME, rule, { }); }); `, - errors: [{ line: 5, messageId: 'noWaitForSnapshot' }], + errors: [ + { + line: 5, + messageId: 'noWaitForSnapshot', + data: { name: asyncUtil }, + column: 27, + }, + ], })), ...ASYNC_UTILS.map((asyncUtil) => ({ code: ` @@ -191,7 +233,14 @@ ruleTester.run(RULE_NAME, rule, { await asyncUtils.${asyncUtil}(() => expect(foo).toMatchInlineSnapshot()); }); `, - errors: [{ line: 4, messageId: 'noWaitForSnapshot' }], + errors: [ + { + line: 4, + messageId: 'noWaitForSnapshot', + data: { name: asyncUtil }, + column: 47 + asyncUtil.length, + }, + ], })), ...ASYNC_UTILS.map((asyncUtil) => ({ code: ` @@ -202,7 +251,14 @@ ruleTester.run(RULE_NAME, rule, { }); }); `, - errors: [{ line: 5, messageId: 'noWaitForSnapshot' }], + errors: [ + { + line: 5, + messageId: 'noWaitForSnapshot', + data: { name: asyncUtil }, + column: 27, + }, + ], })), ], }); From e0bf288c94e6442344a44eebe2f2795fc0435a24 Mon Sep 17 00:00:00 2001 From: Gonzalo D'Elia Date: Sat, 12 Dec 2020 14:42:09 -0300 Subject: [PATCH 2/2] refactor: apply pr suggestions --- lib/rules/no-wait-for-snapshot.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/rules/no-wait-for-snapshot.ts b/lib/rules/no-wait-for-snapshot.ts index f0fb4200..723dfb2a 100644 --- a/lib/rules/no-wait-for-snapshot.ts +++ b/lib/rules/no-wait-for-snapshot.ts @@ -1,6 +1,5 @@ import { ASTUtils, TSESTree } from '@typescript-eslint/experimental-utils'; import { createTestingLibraryRule } from '../create-testing-library-rule'; -import { ASYNC_UTILS } from '../utils'; import { findClosestCallExpressionNode, isMemberExpression, @@ -39,7 +38,7 @@ export default createTestingLibraryRule({ if ( ASTUtils.isIdentifier(callExpression.callee) && helpers.isNodeComingFromTestingLibrary(callExpression.callee) && - ASYNC_UTILS.includes(callExpression.callee.name) + helpers.isAsyncUtil(callExpression.callee) ) { return callExpression.callee; }