Skip to content

Commit

Permalink
feat(rules): add prefer-called-with rule
Browse files Browse the repository at this point in the history
Suggest using `toBeCalledWith()` or `toHaveBeenCalledWith()` instead
of the forms without argument checking.

Similar to prefer-toHaveBeenCalledWith from eslint-plugin-jasmine.

This rule has no opinion on which matcher alias should be used.
  • Loading branch information
bz2 authored and SimenB committed Feb 14, 2019
1 parent a3edd21 commit 6cd30a7
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -119,6 +119,7 @@ for more information about extending configuration files.
| [valid-expect-in-promise][] | Enforce having return statement when testing with promises | ![recommended][] | |
| [valid-expect][] | Enforce valid `expect()` usage | ![recommended][] | |
| [prefer-todo][] | Suggest using `test.todo()` | | ![fixable-green][] |
| [prefer-called-with][] | Suggest using `toBeCalledWith()` OR `toHaveBeenCalledWith()` | | |

## Credit

Expand All @@ -140,6 +141,7 @@ for more information about extending configuration files.
[no-test-prefixes]: docs/rules/no-test-prefixes.md
[no-test-return-statement]: docs/rules/no-test-return-statement.md
[no-truthy-falsy]: docs/rules/no-truthy-falsy.md
[prefer-called-with]: docs/rules/prefer-called-with.md
[prefer-expect-assertions]: docs/rules/prefer-expect-assertions.md
[prefer-spy-on]: docs/rules/prefer-spy-on.md
[prefer-strict-equal]: docs/rules/prefer-strict-equal.md
Expand Down
32 changes: 32 additions & 0 deletions docs/rules/prefer-called-with.md
@@ -0,0 +1,32 @@
# Suggest using `toBeCalledWith` OR `toHaveBeenCalledWith` (prefer-called-with)

The `toBeCalled()` matcher is used to assert that a mock function has been
called one or more times, without checking the arguments passed. The assertion
is stronger when arguments are also validated using the `toBeCalledWith()`
matcher. When some arguments are difficult to check, using generic match like
`expect.anything()` at least enforces number and position of arguments.

This rule warns if the form without argument checking is used, except for `.not`
enforcing a function has never been called.

## Rule details

The following patterns are warnings:

```js
expect(someFunction).toBeCalled();

expect(someFunction).toHaveBeenCalled();
```

The following patterns are not warnings:

```js
expect(noArgsFunction).toBeCalledWith();

expect(roughArgsFunction).toBeCalledWith(expect.anything(), expect.any(Date));

expect(anyArgsFunction).toBeCalledTimes(1);

expect(uncalledFunction).not.toBeCalled();
```
2 changes: 2 additions & 0 deletions index.js
Expand Up @@ -28,6 +28,7 @@ const noAliasMethods = require('./rules/no-alias-methods');
const noTestCallback = require('./rules/no-test-callback');
const noTruthyFalsy = require('./rules/no-truthy-falsy');
const preferTodo = require('./rules/prefer-todo');
const preferCalledWith = require('./rules/prefer-called-with');

const snapshotProcessor = require('./processors/snapshot-processor');

Expand Down Expand Up @@ -116,5 +117,6 @@ module.exports = {
'no-test-callback': noTestCallback,
'no-truthy-falsy': noTruthyFalsy,
'prefer-todo': preferTodo,
'prefer-called-with': preferCalledWith,
},
};
44 changes: 44 additions & 0 deletions rules/__tests__/prefer-called-with.js
@@ -0,0 +1,44 @@
'use strict';

const { RuleTester } = require('eslint');
const rule = require('../prefer-called-with');

const ruleTester = new RuleTester();

ruleTester.run('prefer-called-with', rule, {
valid: [
'expect(fn).toBeCalledWith();',
'expect(fn).toHaveBeenCalledWith();',
'expect(fn).toBeCalledWith(expect.anything());',
'expect(fn).toHaveBeenCalledWith(expect.anything());',
'expect(fn).not.toBeCalled();',
'expect(fn).not.toHaveBeenCalled();',
'expect(fn).not.toBeCalledWith();',
'expect(fn).not.toHaveBeenCalledWith();',
'expect(fn).toBeCalledTimes(0);',
'expect(fn).toHaveBeenCalledTimes(0);',
],

invalid: [
{
code: 'expect(fn).toBeCalled();',
errors: [
{
message: 'Prefer toBeCalledWith(/* expected args */)',
column: 12,
line: 1,
},
],
},
{
code: 'expect(fn).toHaveBeenCalled();',
errors: [
{
message: 'Prefer toHaveBeenCalledWith(/* expected args */)',
column: 12,
line: 1,
},
],
},
],
});
29 changes: 29 additions & 0 deletions rules/prefer-called-with.js
@@ -0,0 +1,29 @@
'use strict';

const { getDocsUrl, expectCase, expectNotCase, method } = require('./util');

module.exports = {
meta: {
docs: {
url: getDocsUrl(__filename),
},
},
create(context) {
return {
CallExpression(node) {
// Could check resolves/rejects here but not a likely idiom.
if (expectCase(node) && !expectNotCase(node)) {
const methodNode = method(node);
const { name } = methodNode;
if (name === 'toBeCalled' || name === 'toHaveBeenCalled') {
context.report({
data: { name },
message: 'Prefer {{name}}With(/* expected args */)',
node: methodNode,
});
}
}
},
};
},
};

0 comments on commit 6cd30a7

Please sign in to comment.