Skip to content

Commit ff34315

Browse files
noahbenhamSimenB
authored andcommitted
feat(rules): add no-truthy-falsy rule
Fixes #204
1 parent 666d50d commit ff34315

File tree

5 files changed

+182
-0
lines changed

5 files changed

+182
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ for more information about extending configuration files.
105105
| [no-test-callback][] | Using a callback in asynchronous tests | | ![fixable-green][] |
106106
| [no-test-prefixes][] | Disallow using `f` & `x` prefixes to define focused/skipped tests | ![recommended][] | ![fixable-green][] |
107107
| [no-test-return-statement][] | Disallow explicitly returning from tests | | |
108+
| [no-truthy-falsy][] | Disallow using `toBeTruthy()` & `toBeFalsy()` | | |
108109
| [prefer-expect-assertions][] | Suggest using `expect.assertions()` OR `expect.hasAssertions()` | | |
109110
| [prefer-spy-on][] | Suggest using `jest.spyOn()` | | ![fixable-green][] |
110111
| [prefer-strict-equal][] | Suggest using `toStrictEqual()` | | ![fixable-green][] |
@@ -137,6 +138,7 @@ for more information about extending configuration files.
137138
[no-test-callback]: docs/rules/no-test-callback.md
138139
[no-test-prefixes]: docs/rules/no-test-prefixes.md
139140
[no-test-return-statement]: docs/rules/no-test-return-statement.md
141+
[no-truthy-falsy]: docs/rules/no-truthy-falsy.md
140142
[prefer-expect-assertions]: docs/rules/prefer-expect-assertions.md
141143
[prefer-spy-on]: docs/rules/prefer-spy-on.md
142144
[prefer-strict-equal]: docs/rules/prefer-strict-equal.md

docs/rules/no-truthy-falsy.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Disallow using `toBeTruthy()` & `toBeFalsy()` (no-truthy-falsy)
2+
3+
Tests against boolean values should assert true or false. Asserting `toBeTruthy`
4+
or `toBeFalsy` matches non-boolean values as well and encourages weaker tests.
5+
6+
For example, `expect(someBoolean).toBeFalsy()` passes when
7+
`someBoolean === null`, and when `someBoolean === false`.
8+
9+
Similarly, `expect(someBoolean).toBeTruthy()` passes when `someBoolean === []`,
10+
and when `someBoolean === 'false'` (note that `'false'` is a string).
11+
12+
## Rule details
13+
14+
This rule triggers a warning if `toBeTruthy()` or `toBeFalsy()` are used.
15+
16+
This rule is disabled by default.
17+
18+
### Default configuration
19+
20+
The following patterns are considered warnings:
21+
22+
```js
23+
expect(someValue).toBeTruthy();
24+
expect(someValue).toBeFalsy();
25+
```
26+
27+
The following patterns are not considered warnings:
28+
29+
```js
30+
expect(someValue).toBe(true);
31+
expect(someValue).toBe(false);
32+
```

index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const preferStrictEqual = require('./rules/prefer-strict-equal');
2626
const requireTothrowMessage = require('./rules/require-tothrow-message');
2727
const noAliasMethods = require('./rules/no-alias-methods');
2828
const noTestCallback = require('./rules/no-test-callback');
29+
const noTruthyFalsy = require('./rules/no-truthy-falsy');
2930

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

@@ -112,5 +113,6 @@ module.exports = {
112113
'require-tothrow-message': requireTothrowMessage,
113114
'no-alias-methods': noAliasMethods,
114115
'no-test-callback': noTestCallback,
116+
'no-truthy-falsy': noTruthyFalsy,
115117
},
116118
};
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
'use strict';
2+
3+
const { RuleTester } = require('eslint');
4+
const rule = require('../no-truthy-falsy');
5+
6+
const ruleTester = new RuleTester();
7+
8+
ruleTester.run('no-truthy-falsy', rule, {
9+
valid: [
10+
'expect(true).toBe(true);',
11+
'expect(false).toBe(false);',
12+
'expect("anything").toBe(true);',
13+
'expect("anything").toEqual(false);',
14+
'expect("anything").not.toBe(true);',
15+
'expect("anything").not.toEqual(true);',
16+
'expect(Promise.resolve({})).resolves.toBe(true);',
17+
'expect(Promise.reject({})).rejects.toBe(true);',
18+
],
19+
20+
invalid: [
21+
{
22+
code: 'expect(true).toBeTruthy();',
23+
errors: [
24+
{
25+
message: 'Avoid toBeTruthy',
26+
column: 14,
27+
line: 1,
28+
},
29+
],
30+
},
31+
{
32+
code: 'expect(false).not.toBeTruthy();',
33+
errors: [
34+
{
35+
message: 'Avoid toBeTruthy',
36+
column: 19,
37+
line: 1,
38+
},
39+
],
40+
},
41+
{
42+
code: 'expect(Promise.resolve({})).resolves.toBeTruthy()',
43+
errors: [
44+
{
45+
message: 'Avoid toBeTruthy',
46+
column: 38,
47+
line: 1,
48+
},
49+
],
50+
},
51+
{
52+
code: 'expect(Promise.resolve({})).rejects.toBeTruthy()',
53+
errors: [
54+
{
55+
message: 'Avoid toBeTruthy',
56+
column: 37,
57+
line: 1,
58+
},
59+
],
60+
},
61+
{
62+
code: 'expect(false).toBeFalsy();',
63+
errors: [
64+
{
65+
message: 'Avoid toBeFalsy',
66+
column: 15,
67+
line: 1,
68+
},
69+
],
70+
},
71+
{
72+
code: 'expect(true).not.toBeFalsy();',
73+
errors: [
74+
{
75+
message: 'Avoid toBeFalsy',
76+
column: 18,
77+
line: 1,
78+
},
79+
],
80+
},
81+
{
82+
code: 'expect(Promise.resolve({})).resolves.toBeFalsy()',
83+
errors: [
84+
{
85+
message: 'Avoid toBeFalsy',
86+
column: 38,
87+
line: 1,
88+
},
89+
],
90+
},
91+
{
92+
code: 'expect(Promise.resolve({})).rejects.toBeFalsy()',
93+
errors: [
94+
{
95+
message: 'Avoid toBeFalsy',
96+
column: 37,
97+
line: 1,
98+
},
99+
],
100+
},
101+
],
102+
});

rules/no-truthy-falsy.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use strict';
2+
3+
const {
4+
getDocsUrl,
5+
expectCase,
6+
expectNotCase,
7+
expectResolveCase,
8+
expectRejectCase,
9+
method,
10+
} = require('./util');
11+
12+
module.exports = {
13+
meta: {
14+
docs: {
15+
url: getDocsUrl(__filename),
16+
},
17+
},
18+
create(context) {
19+
return {
20+
CallExpression(node) {
21+
if (
22+
expectCase(node) ||
23+
expectNotCase(node) ||
24+
expectResolveCase(node) ||
25+
expectRejectCase(node)
26+
) {
27+
const targetNode =
28+
node.parent.parent.type === 'MemberExpression' ? node.parent : node;
29+
30+
const methodNode = method(targetNode);
31+
const { name: methodName } = methodNode;
32+
33+
if (methodName === 'toBeTruthy' || methodName === 'toBeFalsy') {
34+
context.report({
35+
data: { methodName },
36+
message: 'Avoid {{methodName}}',
37+
node: methodNode,
38+
});
39+
}
40+
}
41+
},
42+
};
43+
},
44+
};

0 commit comments

Comments
 (0)