From 10e5087008ac9be34d59d41230c9fadacc27c4f7 Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Tue, 29 Oct 2019 00:37:15 +1300 Subject: [PATCH] Better support valid each (#460) * chore(utils): create `getJestFunctionArguments` utility function * fix(valid-describe): better support `.each` function * fix(valid-title): better support `.each` function --- src/rules/__tests__/valid-describe.test.ts | 25 +++++++++++++++++----- src/rules/__tests__/valid-title.test.ts | 10 +++++++++ src/rules/utils.ts | 20 +++++++++++++++++ src/rules/valid-describe.ts | 18 +++++++--------- src/rules/valid-title.ts | 3 ++- 5 files changed, 60 insertions(+), 16 deletions(-) diff --git a/src/rules/__tests__/valid-describe.test.ts b/src/rules/__tests__/valid-describe.test.ts index 9bb1228bd..e38a8c836 100644 --- a/src/rules/__tests__/valid-describe.test.ts +++ b/src/rules/__tests__/valid-describe.test.ts @@ -11,16 +11,11 @@ const ruleTester = new TSESLint.RuleTester({ ruleTester.run('valid-describe', rule, { valid: [ - 'describe.each()()', - 'describe.each(() => {})()', 'describe["each"]()()', 'describe["each"](() => {})()', 'describe["each"](() => {})("foo")', - 'describe.each(() => {})("foo")', 'describe["each"]()(() => {})', - 'describe.each()(() => {})', 'describe["each"]("foo")(() => {})', - 'describe.each("foo")(() => {})', 'describe("foo", function() {})', 'describe("foo", () => {})', 'describe(`foo`, () => {})', @@ -55,6 +50,26 @@ ruleTester.run('valid-describe', rule, { `, ], invalid: [ + { + code: 'describe.each()()', + errors: [{ messageId: 'nameAndCallback', line: 1, column: 1 }], + }, + { + code: 'describe.each(() => {})()', + errors: [{ messageId: 'nameAndCallback', line: 1, column: 1 }], + }, + { + code: 'describe.each(() => {})("foo")', + errors: [{ messageId: 'nameAndCallback', line: 1, column: 25 }], + }, + { + code: 'describe.each()(() => {})', + errors: [{ messageId: 'nameAndCallback', line: 1, column: 17 }], + }, + { + code: 'describe.each("foo")(() => {})', + errors: [{ messageId: 'nameAndCallback', line: 1, column: 22 }], + }, { code: 'describe(() => {})', errors: [{ messageId: 'nameAndCallback', line: 1, column: 10 }], diff --git a/src/rules/__tests__/valid-title.test.ts b/src/rules/__tests__/valid-title.test.ts index abbf81ee7..534e5b0eb 100644 --- a/src/rules/__tests__/valid-title.test.ts +++ b/src/rules/__tests__/valid-title.test.ts @@ -33,6 +33,16 @@ ruleTester.run('title-must-be-string', rule, { }, ], invalid: [ + { + code: 'it.each([])(1, () => {});', + errors: [ + { + messageId: 'titleMustBeString', + column: 13, + line: 1, + }, + ], + }, { code: 'it(123, () => {});', errors: [ diff --git a/src/rules/utils.ts b/src/rules/utils.ts index 85b71e58a..c78cf5349 100644 --- a/src/rules/utils.ts +++ b/src/rules/utils.ts @@ -651,6 +651,26 @@ export const isDescribe = ( ); }; +/** + * Gets the arguments of the given `JestFunctionCallExpression`. + * + * If the `node` is an `each` call, then the arguments of the actual suite + * are returned, rather then the `each` array argument. + * + * @param {JestFunctionCallExpression} node + * + * @return {Expression[]} + */ +export const getJestFunctionArguments = ( + node: JestFunctionCallExpression, +) => + node.callee.type === AST_NODE_TYPES.MemberExpression && + isSupportedAccessor(node.callee.property, DescribeProperty.each) && + node.parent && + node.parent.type === AST_NODE_TYPES.CallExpression + ? node.parent.arguments + : node.arguments; + const collectReferences = (scope: TSESLint.Scope.Scope) => { const locals = new Set(); const unresolved = new Set(); diff --git a/src/rules/valid-describe.ts b/src/rules/valid-describe.ts index bacbda288..d2a8ad83d 100644 --- a/src/rules/valid-describe.ts +++ b/src/rules/valid-describe.ts @@ -4,9 +4,9 @@ import { } from '@typescript-eslint/experimental-utils'; import { createRule, + getJestFunctionArguments, isDescribe, isFunction, - isSupportedAccessor, } from './utils'; const paramsLocation = ( @@ -21,10 +21,6 @@ const paramsLocation = ( }; }; -const isDescribeEach = (node: TSESTree.CallExpression) => - node.callee.type === AST_NODE_TYPES.MemberExpression && - isSupportedAccessor(node.callee.property, 'each'); - export default createRule({ name: __filename, meta: { @@ -49,23 +45,25 @@ export default createRule({ create(context) { return { CallExpression(node) { - if (!isDescribe(node) || isDescribeEach(node)) { + if (!isDescribe(node)) { return; } - if (node.arguments.length < 1) { + const nodeArguments = getJestFunctionArguments(node); + + if (nodeArguments.length < 1) { return context.report({ messageId: 'nameAndCallback', loc: node.loc, }); } - const [, callback] = node.arguments; + const [, callback] = nodeArguments; if (!callback) { context.report({ messageId: 'nameAndCallback', - loc: paramsLocation(node.arguments), + loc: paramsLocation(nodeArguments), }); return; @@ -74,7 +72,7 @@ export default createRule({ if (!isFunction(callback)) { context.report({ messageId: 'secondArgumentMustBeFunction', - loc: paramsLocation(node.arguments), + loc: paramsLocation(nodeArguments), }); return; diff --git a/src/rules/valid-title.ts b/src/rules/valid-title.ts index c0cd8ab3b..4c9354798 100644 --- a/src/rules/valid-title.ts +++ b/src/rules/valid-title.ts @@ -6,6 +6,7 @@ import { DescribeAlias, TestCaseName, createRule, + getJestFunctionArguments, getNodeName, getStringValue, isDescribe, @@ -53,7 +54,7 @@ export default createRule({ return; } - const [argument] = node.arguments; + const [argument] = getJestFunctionArguments(node); if (!isStringNode(argument)) { if (