From 2d584d426be77d400a3d5da6d11e2a6acbca10dc Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Sun, 5 Jun 2022 10:21:02 +1200 Subject: [PATCH] fix(no-restricted-matchers): allow restricting negated `resolves` and `rejects` modifiers --- .../__tests__/no-restricted-matchers.test.ts | 76 +++++++++++++++ src/rules/no-restricted-matchers.ts | 97 ++++++++++--------- 2 files changed, 129 insertions(+), 44 deletions(-) diff --git a/src/rules/__tests__/no-restricted-matchers.test.ts b/src/rules/__tests__/no-restricted-matchers.test.ts index f08383102..eb7c84124 100644 --- a/src/rules/__tests__/no-restricted-matchers.test.ts +++ b/src/rules/__tests__/no-restricted-matchers.test.ts @@ -99,6 +99,66 @@ ruleTester.run('no-restricted-matchers', rule, { }, ], }, + { + code: 'expect(a).resolves.toBe(b)', + options: [{ resolves: null }], + errors: [ + { + messageId: 'restrictedChain', + data: { + message: null, + chain: 'resolves', + }, + column: 11, + line: 1, + }, + ], + }, + { + code: 'expect(a).resolves.not.toBe(b)', + options: [{ not: null }], + errors: [ + { + messageId: 'restrictedChain', + data: { + message: null, + chain: 'not', + }, + column: 20, + line: 1, + }, + ], + }, + { + code: 'expect(a).resolves.not.toBe(b)', + options: [{ resolves: null }], + errors: [ + { + messageId: 'restrictedChain', + data: { + message: null, + chain: 'resolves', + }, + column: 11, + line: 1, + }, + ], + }, + { + code: 'expect(a).resolves.not.toBe(b)', + options: [{ 'resolves.not': null }], + errors: [ + { + messageId: 'restrictedChain', + data: { + message: null, + chain: 'resolves.not', + }, + column: 11, + line: 1, + }, + ], + }, { code: 'expect(a).not.toBe(b)', options: [{ 'not.toBe': null }], @@ -115,6 +175,22 @@ ruleTester.run('no-restricted-matchers', rule, { }, ], }, + { + code: 'expect(a).resolves.not.toBe(b)', + options: [{ 'resolves.not.toBe': null }], + errors: [ + { + messageId: 'restrictedChain', + data: { + message: null, + chain: 'resolves.not.toBe', + }, + endColumn: 28, + column: 11, + line: 1, + }, + ], + }, { code: 'expect(a).toBe(b)', options: [{ toBe: 'Prefer `toStrictEqual` instead' }], diff --git a/src/rules/no-restricted-matchers.ts b/src/rules/no-restricted-matchers.ts index d439ecd5b..3998e197f 100644 --- a/src/rules/no-restricted-matchers.ts +++ b/src/rules/no-restricted-matchers.ts @@ -1,3 +1,4 @@ +import { TSESTree } from '@typescript-eslint/utils'; import { createRule, isExpectCall, parseExpectCall } from './utils'; export default createRule< @@ -27,6 +28,25 @@ export default createRule< }, defaultOptions: [{}], create(context, [restrictedChains]) { + const reportIfRestricted = ( + loc: TSESTree.SourceLocation, + chain: string, + ): boolean => { + if (!(chain in restrictedChains)) { + return false; + } + + const message = restrictedChains[chain]; + + context.report({ + messageId: message ? 'restrictedChainWithMessage' : 'restrictedChain', + data: { message, chain }, + loc, + }); + + return true; + }; + return { CallExpression(node) { if (!isExpectCall(node)) { @@ -35,61 +55,50 @@ export default createRule< const { matcher, modifier } = parseExpectCall(node); - if (matcher) { - const chain = matcher.name; - - if (chain in restrictedChains) { - const message = restrictedChains[chain]; - - context.report({ - messageId: message - ? 'restrictedChainWithMessage' - : 'restrictedChain', - data: { message, chain }, - node: matcher.node.property, - }); - - return; - } + if ( + matcher && + reportIfRestricted(matcher.node.property.loc, matcher.name) + ) { + return; } if (modifier) { - const chain = modifier.name; - - if (chain in restrictedChains) { - const message = restrictedChains[chain]; - - context.report({ - messageId: message - ? 'restrictedChainWithMessage' - : 'restrictedChain', - data: { message, chain }, - node: modifier.node.property, - }); - + if (reportIfRestricted(modifier.node.property.loc, modifier.name)) { return; } + + if (modifier.negation) { + if ( + reportIfRestricted(modifier.negation.property.loc, 'not') || + reportIfRestricted( + { + start: modifier.node.property.loc.start, + end: modifier.negation.property.loc.end, + }, + `${modifier.name}.not`, + ) + ) { + return; + } + } } if (matcher && modifier) { - const chain = `${modifier.name}.${matcher.name}`; + let chain: string = modifier.name; - if (chain in restrictedChains) { - const message = restrictedChains[chain]; + if (modifier.negation) { + chain += '.not'; + } - context.report({ - messageId: message - ? 'restrictedChainWithMessage' - : 'restrictedChain', - data: { message, chain }, - loc: { - start: modifier.node.property.loc.start, - end: matcher.node.property.loc.end, - }, - }); + chain += `.${matcher.name}`; - return; - } + reportIfRestricted( + { + start: modifier.node.property.loc.start, + end: matcher.node.property.loc.end, + }, + chain, + ); } }, };