Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(require-tothrow-message): require throw messages on async functions #303

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/rules/require-tothrow-message.md
Expand Up @@ -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:
Expand All @@ -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');
```
57 changes: 56 additions & 1 deletion src/rules/__tests__/require-tothrow-message.test.js
Expand Up @@ -5,7 +5,7 @@ const rule = require('../require-tothrow-message');

const ruleTester = new RuleTester({
parserOptions: {
ecmaVersion: 6,
ecmaVersion: 8,
},
});

Expand All @@ -14,19 +14,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: [
Expand Down Expand Up @@ -54,5 +86,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,
},
],
},
],
});
11 changes: 8 additions & 3 deletions src/rules/require-tothrow-message.js
Expand Up @@ -19,17 +19,22 @@ module.exports = {
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,
});
}
},
Expand Down