From 3ee38742e0d235196619488a88fe679d1e8c0030 Mon Sep 17 00:00:00 2001 From: Chris Blossom Date: Thu, 18 Jul 2019 13:37:52 -0700 Subject: [PATCH] fix(require-tothrow-message): require throw messages on async functions (#303) --- docs/rules/require-tothrow-message.md | 8 +++ .../__tests__/require-tothrow-message.test.js | 57 ++++++++++++++++++- src/rules/require-tothrow-message.js | 11 +++- 3 files changed, 72 insertions(+), 4 deletions(-) diff --git a/docs/rules/require-tothrow-message.md b/docs/rules/require-tothrow-message.md index 444d36437..1a20e9e30 100644 --- a/docs/rules/require-tothrow-message.md +++ b/docs/rules/require-tothrow-message.md @@ -18,6 +18,10 @@ The following patterns are considered warnings: expect(() => a()).toThrow(); expect(() => a()).toThrowError(); + +await expect(a()).rejects.toThrow(); + +await expect(a()).rejects.toThrowError(); ``` The following patterns are not considered warnings: @@ -26,4 +30,8 @@ The following patterns are not considered warnings: expect(() => a()).toThrow('a'); expect(() => a()).toThrowError('a'); + +await expect(a()).rejects.toThrow('a'); + +await expect(a()).rejects.toThrowError('a'); ``` diff --git a/src/rules/__tests__/require-tothrow-message.test.js b/src/rules/__tests__/require-tothrow-message.test.js index 25c6fe58c..c91a39cdb 100644 --- a/src/rules/__tests__/require-tothrow-message.test.js +++ b/src/rules/__tests__/require-tothrow-message.test.js @@ -3,7 +3,7 @@ import rule from '../require-tothrow-message'; const ruleTester = new RuleTester({ parserOptions: { - ecmaVersion: 6, + ecmaVersion: 8, }, }); @@ -12,19 +12,51 @@ ruleTester.run('require-tothrow-message', rule, { // String "expect(() => { throw new Error('a'); }).toThrow('a');", "expect(() => { throw new Error('a'); }).toThrowError('a');", + `test('string', async () => { + const throwErrorAsync = async () => { throw new Error('a') }; + await expect(throwErrorAsync()).rejects.toThrow('a'); + await expect(throwErrorAsync()).rejects.toThrowError('a'); + })`, // Template literal "const a = 'a'; expect(() => { throw new Error('a'); }).toThrow(`${a}`);", + "const a = 'a'; expect(() => { throw new Error('a'); }).toThrowError(`${a}`);", + `test('Template literal', async () => { + const a = 'a'; + const throwErrorAsync = async () => { throw new Error('a') }; + await expect(throwErrorAsync()).rejects.toThrow(\`\${a}\`); + await expect(throwErrorAsync()).rejects.toThrowError(\`\${a}\`); + })`, // Regex "expect(() => { throw new Error('a'); }).toThrow(/^a$/);", + "expect(() => { throw new Error('a'); }).toThrowError(/^a$/);", + `test('Regex', async () => { + const throwErrorAsync = async () => { throw new Error('a') }; + await expect(throwErrorAsync()).rejects.toThrow(/^a$/); + await expect(throwErrorAsync()).rejects.toThrowError(/^a$/); + })`, // Function "expect(() => { throw new Error('a'); })" + ".toThrow((() => { return 'a'; })());", + "expect(() => { throw new Error('a'); })" + + ".toThrowError((() => { return 'a'; })());", + `test('Function', async () => { + const throwErrorAsync = async () => { throw new Error('a') }; + const fn = () => { return 'a'; }; + await expect(throwErrorAsync()).rejects.toThrow(fn()); + await expect(throwErrorAsync()).rejects.toThrowError(fn()); + })`, // Allow no message for `not`. "expect(() => { throw new Error('a'); }).not.toThrow();", + "expect(() => { throw new Error('a'); }).not.toThrowError();", + `test('Allow no message for "not"', async () => { + const throwErrorAsync = async () => { throw new Error('a') }; + await expect(throwErrorAsync()).resolves.not.toThrow(); + await expect(throwErrorAsync()).resolves.not.toThrowError(); + })`, ], invalid: [ @@ -52,5 +84,28 @@ ruleTester.run('require-tothrow-message', rule, { }, ], }, + + // Empty rejects.toThrow / rejects.toThrowError + { + code: `test('empty rejects.toThrow', async () => { + const throwErrorAsync = async () => { throw new Error('a') }; + await expect(throwErrorAsync()).rejects.toThrow(); + await expect(throwErrorAsync()).rejects.toThrowError(); + })`, + errors: [ + { + messageId: 'requireRethrow', + data: { propertyName: 'toThrow' }, + column: 49, + line: 3, + }, + { + messageId: 'requireRethrow', + data: { propertyName: 'toThrowError' }, + column: 49, + line: 4, + }, + ], + }, ], }); diff --git a/src/rules/require-tothrow-message.js b/src/rules/require-tothrow-message.js index 0ae027775..449a348a9 100644 --- a/src/rules/require-tothrow-message.js +++ b/src/rules/require-tothrow-message.js @@ -17,17 +17,22 @@ export default { return; } - const propertyName = method(node) && method(node).name; + let targetNode = method(node); + if (targetNode.name === 'rejects') { + targetNode = method(node.parent); + } + + const propertyName = method(targetNode) && method(targetNode).name; // Look for `toThrow` calls with no arguments. if ( ['toThrow', 'toThrowError'].includes(propertyName) && - !argument(node) + !argument(targetNode) ) { context.report({ messageId: 'requireRethrow', data: { propertyName }, - node: method(node), + node: targetNode, }); } },