Skip to content

Commit

Permalink
fix(require-tothrow-message): require throw messages on async functio…
Browse files Browse the repository at this point in the history
…ns (#303)
  • Loading branch information
chrisblossom authored and SimenB committed Jul 18, 2019
1 parent b3a360d commit 3ee3874
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 4 deletions.
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 @@ -3,7 +3,7 @@ import rule from '../require-tothrow-message';

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

Expand All @@ -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: [
Expand Down Expand Up @@ -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,
},
],
},
],
});
11 changes: 8 additions & 3 deletions src/rules/require-tothrow-message.js
Expand Up @@ -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,
});
}
},
Expand Down

0 comments on commit 3ee3874

Please sign in to comment.