From f41d5c4bcaa3fb15377df28f620db7c623635ac8 Mon Sep 17 00:00:00 2001 From: Eran Shabi Date: Wed, 7 Aug 2019 19:18:25 +0300 Subject: [PATCH] feat(rules): no-expect-resolves (#364) --- README.md | 2 + docs/rules/no-expect-resolves.md | 29 ++++++++++++++ src/__tests__/rules.test.ts | 2 +- .../__tests__/no-expect-resolves.test.ts | 24 +++++++++++ src/rules/no-expect-resolves.ts | 40 +++++++++++++++++++ 5 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 docs/rules/no-expect-resolves.md create mode 100644 src/rules/__tests__/no-expect-resolves.test.ts create mode 100644 src/rules/no-expect-resolves.ts diff --git a/README.md b/README.md index 3c6e9616b..cb8ee5a72 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,7 @@ installations requiring long-term consistency. | [no-disabled-tests][] | Disallow disabled tests | ![recommended][] | | | [no-duplicate-hooks][] | Disallow duplicate hooks within a `describe` block | | | | [no-empty-title][] | Disallow empty titles | | | +| [no-expect-resolves][] | Disallow using `expect().resolves` | | | | [no-export][] | Disallow export from test files | | | | [no-focused-tests][] | Disallow focused tests | ![recommended][] | | | [no-hooks][] | Disallow setup and teardown hooks | | | @@ -166,6 +167,7 @@ https://github.com/dangreenisrael/eslint-plugin-jest-formatting [no-disabled-tests]: docs/rules/no-disabled-tests.md [no-duplicate-hooks]: docs/rules/no-duplicate-hooks.md [no-empty-title]: docs/rules/no-empty-title.md +[no-expect-resolves]: docs/rules/no-expect-resolves.md [no-export]: docs/rules/no-export.md [no-focused-tests]: docs/rules/no-focused-tests.md [no-hooks]: docs/rules/no-hooks.md diff --git a/docs/rules/no-expect-resolves.md b/docs/rules/no-expect-resolves.md new file mode 100644 index 000000000..c15d08300 --- /dev/null +++ b/docs/rules/no-expect-resolves.md @@ -0,0 +1,29 @@ +# Avoid using `expect().resolves` (no-expect-resolves) + +Jest allows you to test a promise resolve value using `await expect().resolves`. +For consistency and readability this rule bans `expect().resolves` in favor of +`expect(await promise)`. + +## Rule details + +This rule triggers a warning if `expect().resolves` is used. + +This rule is disabled by default. + +### Default configuration + +The following patterns is considered warning: + +```js +test('some test', async () => { + await expect(Promise.resolve(1)).resolves.toBe(1); +}); +``` + +The following pattern is not considered warning: + +```js +test('some test', async () => { + expect(await Promise.resolve(1)).toBe(1); +}); +``` diff --git a/src/__tests__/rules.test.ts b/src/__tests__/rules.test.ts index 7eaaed181..7b4021486 100644 --- a/src/__tests__/rules.test.ts +++ b/src/__tests__/rules.test.ts @@ -4,7 +4,7 @@ import { resolve } from 'path'; import { rules } from '../'; const ruleNames = Object.keys(rules); -const numberOfRules = 37; +const numberOfRules = 38; describe('rules', () => { it('should have a corresponding doc for each rule', () => { diff --git a/src/rules/__tests__/no-expect-resolves.test.ts b/src/rules/__tests__/no-expect-resolves.test.ts new file mode 100644 index 000000000..c983499b5 --- /dev/null +++ b/src/rules/__tests__/no-expect-resolves.test.ts @@ -0,0 +1,24 @@ +import { TSESLint } from '@typescript-eslint/experimental-utils'; +import rule from '../no-expect-resolves'; + +const ruleTester = new TSESLint.RuleTester({ + parserOptions: { + ecmaVersion: 2017, + }, +}); + +ruleTester.run('no-expect-resolves', rule, { + valid: [ + `test('some test', async () => { + expect(await Promise.resolve(1)).toBe(1); + });`, + ], + invalid: [ + { + code: `test('some test', async () => { + await expect(Promise.resolve(1)).resolves.toBe(1); + });`, + errors: [{ endColumn: 55, column: 47, messageId: 'expectResolves' }], + }, + ], +}); diff --git a/src/rules/no-expect-resolves.ts b/src/rules/no-expect-resolves.ts new file mode 100644 index 000000000..586ca26c2 --- /dev/null +++ b/src/rules/no-expect-resolves.ts @@ -0,0 +1,40 @@ +import { + AST_NODE_TYPES, + TSESTree, +} from '@typescript-eslint/experimental-utils'; +import { createRule, isExpectCall } from './tsUtils'; + +function isIdentifierResolves(node: TSESTree.MemberExpression) { + return ( + node.property.type === AST_NODE_TYPES.Identifier && + node.property.name === 'resolves' + ); +} + +function isExpectResolves(node: TSESTree.MemberExpression) { + return isExpectCall(node.object) && isIdentifierResolves(node); +} + +export default createRule({ + name: __filename, + meta: { + docs: { + category: 'Best Practices', + description: 'Disallow expect.resolves', + recommended: false, + }, + messages: { + expectResolves: 'Use `expect(await promise)` instead.', + }, + schema: [], + type: 'suggestion', + }, + defaultOptions: [], + create: context => ({ + MemberExpression(node) { + if (isExpectResolves(node)) { + context.report({ node: node.property, messageId: 'expectResolves' }); + } + }, + }), +});