From ae8ecac847245d5131e420d0efebfc36b202787a Mon Sep 17 00:00:00 2001 From: Hasegawa-Yukihiro Date: Sun, 12 May 2024 15:51:23 +0900 Subject: [PATCH] fix: valid-expect logic --- src/rules/__tests__/valid-expect.test.ts | 168 ++++++++++++++++++++++- src/rules/valid-expect.ts | 39 +++--- 2 files changed, 188 insertions(+), 19 deletions(-) diff --git a/src/rules/__tests__/valid-expect.test.ts b/src/rules/__tests__/valid-expect.test.ts index 351eee7cb..555ad3d6d 100644 --- a/src/rules/__tests__/valid-expect.test.ts +++ b/src/rules/__tests__/valid-expect.test.ts @@ -436,6 +436,11 @@ ruleTester.run('valid-expect', rule, { }); `, errors: [ + { + column: 12, + endColumn: 4, + messageId: 'asyncMustBeAwaited', + }, { column: 9, endColumn: 43, @@ -463,6 +468,11 @@ ruleTester.run('valid-expect', rule, { }); `, errors: [ + { + column: 12, + endColumn: 4, + messageId: 'asyncMustBeAwaited', + }, { column: 9, endColumn: 43, @@ -494,6 +504,11 @@ ruleTester.run('valid-expect', rule, { }); `, errors: [ + { + column: 12, + endColumn: 4, + messageId: 'asyncMustBeAwaited', + }, { column: 9, endColumn: 43, @@ -508,6 +523,12 @@ ruleTester.run('valid-expect', rule, { output: 'test("valid-expect", async () => { await expect(Promise.resolve(2)).resolves.toBeDefined(); });', errors: [ + { + column: 22, + endColumn: 82, + messageId: 'asyncMustBeAwaited', + data: { orReturned: ' or returned' }, + }, { column: 30, endColumn: 79, @@ -521,6 +542,12 @@ ruleTester.run('valid-expect', rule, { output: 'test("valid-expect", async () => { await expect(Promise.resolve(2)).toResolve(); });', errors: [ + { + messageId: 'asyncMustBeAwaited', + data: { orReturned: ' or returned' }, + column: 22, + line: 1, + }, { messageId: 'asyncMustBeAwaited', data: { orReturned: ' or returned' }, @@ -535,6 +562,12 @@ ruleTester.run('valid-expect', rule, { 'test("valid-expect", async () => { await expect(Promise.resolve(2)).toResolve(); });', options: [{ asyncMatchers: undefined }], errors: [ + { + messageId: 'asyncMustBeAwaited', + data: { orReturned: ' or returned' }, + column: 22, + line: 1, + }, { messageId: 'asyncMustBeAwaited', data: { orReturned: ' or returned' }, @@ -548,6 +581,12 @@ ruleTester.run('valid-expect', rule, { output: 'test("valid-expect", async () => { await expect(Promise.resolve(2)).toReject(); });', errors: [ + { + messageId: 'asyncMustBeAwaited', + data: { orReturned: ' or returned' }, + column: 22, + line: 1, + }, { messageId: 'asyncMustBeAwaited', data: { orReturned: ' or returned' }, @@ -561,6 +600,12 @@ ruleTester.run('valid-expect', rule, { output: 'test("valid-expect", async () => { await expect(Promise.resolve(2)).not.toReject(); });', errors: [ + { + messageId: 'asyncMustBeAwaited', + data: { orReturned: ' or returned' }, + column: 22, + line: 1, + }, { messageId: 'asyncMustBeAwaited', data: { orReturned: ' or returned' }, @@ -575,6 +620,12 @@ ruleTester.run('valid-expect', rule, { output: 'test("valid-expect", async () => { await expect(Promise.resolve(2)).resolves.not.toBeDefined(); });', errors: [ + { + column: 22, + endColumn: 86, + messageId: 'asyncMustBeAwaited', + data: { orReturned: ' or returned' }, + }, { column: 30, endColumn: 83, @@ -589,6 +640,12 @@ ruleTester.run('valid-expect', rule, { output: 'test("valid-expect", async () => { await expect(Promise.resolve(2)).rejects.toBeDefined(); });', errors: [ + { + column: 22, + endColumn: 81, + messageId: 'asyncMustBeAwaited', + data: { orReturned: ' or returned' }, + }, { column: 30, endColumn: 78, @@ -603,6 +660,12 @@ ruleTester.run('valid-expect', rule, { output: 'test("valid-expect", async () => { await expect(Promise.resolve(2)).rejects.not.toBeDefined(); });', errors: [ + { + column: 22, + endColumn: 85, + messageId: 'asyncMustBeAwaited', + data: { orReturned: ' or returned' }, + }, { column: 30, endColumn: 82, @@ -644,6 +707,11 @@ ruleTester.run('valid-expect', rule, { 'test("valid-expect", async () => { await expect(Promise.reject(2)).toRejectWith(2); });', options: [{ asyncMatchers: ['toRejectWith'] }], errors: [ + { + messageId: 'asyncMustBeAwaited', + data: { orReturned: ' or returned' }, + column: 22, + }, { messageId: 'asyncMustBeAwaited', data: { orReturned: ' or returned' }, @@ -657,6 +725,11 @@ ruleTester.run('valid-expect', rule, { 'test("valid-expect", async () => { await expect(Promise.reject(2)).rejects.toBe(2); });', options: [{ asyncMatchers: ['toRejectWith'] }], errors: [ + { + messageId: 'asyncMustBeAwaited', + data: { orReturned: ' or returned' }, + column: 22, + }, { messageId: 'asyncMustBeAwaited', data: { orReturned: ' or returned' }, @@ -838,6 +911,13 @@ ruleTester.run('valid-expect', rule, { }); `, errors: [ + { + line: 1, + column: 22, + endColumn: 2, + messageId: 'promisesWithAsyncAssertionsMustBeAwaited', + data: { orReturned: ' or returned' }, + }, { line: 2, column: 3, @@ -859,6 +939,13 @@ ruleTester.run('valid-expect', rule, { }); `, errors: [ + { + line: 1, + column: 22, + endColumn: 2, + messageId: 'promisesWithAsyncAssertionsMustBeAwaited', + data: { orReturned: ' or returned' }, + }, { line: 2, column: 3, @@ -901,6 +988,13 @@ ruleTester.run('valid-expect', rule, { }); `, errors: [ + { + line: 1, + column: 22, + endColumn: 2, + messageId: 'promisesWithAsyncAssertionsMustBeAwaited', + data: { orReturned: ' or returned' }, + }, { line: 2, column: 3, @@ -924,6 +1018,12 @@ ruleTester.run('valid-expect', rule, { `, options: [{ alwaysAwait: true }], errors: [ + { + line: 1, + column: 22, + endColumn: 2, + messageId: 'promisesWithAsyncAssertionsMustBeAwaited', + }, { line: 2, column: 3, @@ -951,6 +1051,14 @@ ruleTester.run('valid-expect', rule, { }); `, errors: [ + { + line: 1, + column: 22, + endLine: 6, + endColumn: 2, + messageId: 'promisesWithAsyncAssertionsMustBeAwaited', + data: { orReturned: ' or returned' }, + }, { line: 2, column: 3, @@ -980,6 +1088,14 @@ ruleTester.run('valid-expect', rule, { }); `, errors: [ + { + line: 1, + column: 22, + endLine: 6, + endColumn: 2, + messageId: 'promisesWithAsyncAssertionsMustBeAwaited', + data: { orReturned: ' or returned' }, + }, { line: 2, column: 3, @@ -1008,6 +1124,22 @@ ruleTester.run('valid-expect', rule, { }); `, errors: [ + { + line: 1, + column: 22, + endLine: 6, + endColumn: 2, + messageId: 'asyncMustBeAwaited', + data: { orReturned: ' or returned' }, + }, + { + line: 1, + column: 22, + endLine: 6, + endColumn: 2, + messageId: 'asyncMustBeAwaited', + data: { orReturned: ' or returned' }, + }, { line: 3, column: 5, @@ -1038,12 +1170,24 @@ ruleTester.run('valid-expect', rule, { output: dedent` test("valid-expect", async () => { const assertions = [ - expect(Promise.resolve(2)).toResolve(), - expect(Promise.resolve(3)).toReject(), + await expect(Promise.resolve(2)).toResolve(), + await expect(Promise.resolve(3)).toReject(), ] }); `, errors: [ + { + messageId: 'asyncMustBeAwaited', + data: { orReturned: ' or returned' }, + column: 22, + line: 1, + }, + { + messageId: 'asyncMustBeAwaited', + data: { orReturned: ' or returned' }, + column: 22, + line: 1, + }, { messageId: 'asyncMustBeAwaited', data: { orReturned: ' or returned' }, @@ -1076,6 +1220,18 @@ ruleTester.run('valid-expect', rule, { }); `, errors: [ + { + messageId: 'asyncMustBeAwaited', + data: { orReturned: ' or returned' }, + column: 22, + line: 1, + }, + { + messageId: 'asyncMustBeAwaited', + data: { orReturned: ' or returned' }, + column: 22, + line: 1, + }, { messageId: 'asyncMustBeAwaited', data: { orReturned: ' or returned' }, @@ -1116,6 +1272,14 @@ ruleTester.run('valid-expect', rule, { }); `, errors: [ + { + line: 2, + column: 71, + endLine: 4, + endColumn: 4, + messageId: 'asyncMustBeAwaited', + data: { orReturned: ' or returned' }, + }, { line: 3, column: 5, diff --git a/src/rules/valid-expect.ts b/src/rules/valid-expect.ts index 784993f37..e5920fc06 100644 --- a/src/rules/valid-expect.ts +++ b/src/rules/valid-expect.ts @@ -4,7 +4,6 @@ */ import { AST_NODE_TYPES, type TSESTree } from '@typescript-eslint/utils'; -import type { RuleFix } from '@typescript-eslint/utils/ts-eslint'; import { type FunctionExpression, ModifierName, @@ -362,6 +361,25 @@ export default createRule<[Options], MessageIds>({ // if we didn't warn user already !promiseArrayExceptionExists(finalNode.loc) ) { + const functionExpression = findFirstFunctionExpression(finalNode); + + if (functionExpression && !functionExpression.async) { + context.report({ + loc: functionExpression.loc, + data: { orReturned }, + messageId: + finalNode === targetNode + ? 'asyncMustBeAwaited' + : 'promisesWithAsyncAssertionsMustBeAwaited', + node, + fix(fixer) { + const targetFunction = + getNormalizeFunctionExpression(functionExpression); + + return fixer.insertTextBefore(targetFunction, 'async '); + }, + }); + } context.report({ loc: finalNode.loc, data: { orReturned }, @@ -371,20 +389,10 @@ export default createRule<[Options], MessageIds>({ : 'promisesWithAsyncAssertionsMustBeAwaited', node, fix(fixer) { - const functionExpression = findFirstFunctionExpression(finalNode); - if (!functionExpression) { - return []; + return null; } - const fixes: RuleFix[] = []; - - if (!functionExpression.async) { - const targetFunction = - getNormalizeFunctionExpression(functionExpression); - - fixes.push(fixer.insertTextBefore(targetFunction, 'async ')); - } const returnStatement = finalNode.parent.type === AST_NODE_TYPES.ReturnStatement ? finalNode.parent @@ -395,13 +403,10 @@ export default createRule<[Options], MessageIds>({ getSourceCode(context).getText(returnStatement); const replacedText = sourceCodeText.replace('return', 'await'); - return [ - ...fixes, - fixer.replaceText(returnStatement, replacedText), - ]; + return fixer.replaceText(returnStatement, replacedText); } - return [...fixes, fixer.insertTextBefore(finalNode, 'await ')]; + return fixer.insertTextBefore(finalNode, 'await '); }, });