Skip to content

Commit 83bb4bb

Browse files
committed
feat: add new rule no-only-tests
1 parent 65cfb2c commit 83bb4bb

File tree

4 files changed

+190
-0
lines changed

4 files changed

+190
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ Name | ✔️ | 🛠 | Description
5353
[no-deprecated-report-api](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/no-deprecated-report-api.md) | ✔️ | 🛠 | disallow use of the deprecated context.report() API
5454
[no-identical-tests](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/no-identical-tests.md) | ✔️ | 🛠 | disallow identical tests
5555
[no-missing-placeholders](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/no-missing-placeholders.md) | ✔️ | | disallow missing placeholders in rule report messages
56+
[no-only-tests](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/no-only-tests.md) | | | disallow the test case property `only`
5657
[no-unused-placeholders](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/no-unused-placeholders.md) | ✔️ | | disallow unused placeholders in rule report messages
5758
[no-useless-token-range](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/no-useless-token-range.md) | ✔️ | 🛠 | disallow unnecessary calls to sourceCode.getFirstToken and sourceCode.getLastToken
5859
[prefer-object-rule](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/prefer-object-rule.md) | | 🛠 | disallow rule exports where the export is a function.

docs/rules/no-only-tests.md

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Disallow the test case property `only` (no-only-tests)
2+
3+
The [`only` property](https://eslint.org/docs/developer-guide/unit-tests#running-individual-tests) can be used as of [ESLint 7.29](https://eslint.org/blog/2021/06/eslint-v7.29.0-released#highlights) for running individual rule test cases with less-noisy debugging. This feature should be only used in development, as it prevents all the tests from running. Mistakenly checking-in a test case with this property can cause CI tests to incorrectly pass.
4+
5+
## Rule Details
6+
7+
This rule flags a violation when a test case is using `only`. Note that this rule is not autofixable since automatically deleting the property would prevent developers from being able to use it during development.
8+
9+
Examples of **incorrect** code for this rule:
10+
11+
```js
12+
/* eslint eslint-plugin/no-only-tests: error */
13+
14+
ruleTester.run('my-rule', myRule, {
15+
valid: [
16+
{
17+
code: 'const valid = 42;',
18+
only: true,
19+
},
20+
RuleTester.only('const valid = 42;'),
21+
],
22+
invalid: [
23+
{
24+
code: 'const invalid = 42;',
25+
only: true,
26+
errors: [/* ... */],
27+
},
28+
],
29+
});
30+
```
31+
32+
Examples of **correct** code for this rule:
33+
34+
```js
35+
/* eslint eslint-plugin/no-only-tests: error */
36+
37+
ruleTester.run('my-rule', myRule, {
38+
valid: [
39+
'const valid = 42;',
40+
{ code: 'const valid = 42;' }
41+
],
42+
invalid: [
43+
{
44+
code: 'const invalid = 42;',
45+
errors: [/* ... */],
46+
},
47+
],
48+
});
49+
```

lib/rules/no-only-tests.js

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
'use strict';
2+
3+
const utils = require('../utils');
4+
5+
module.exports = {
6+
meta: {
7+
type: 'problem',
8+
docs: {
9+
description: 'disallow the test case property `only`',
10+
category: 'Tests',
11+
recommended: false,
12+
},
13+
schema: [],
14+
messages: {
15+
foundOnly:
16+
'The test case property `only` can be used during development, but should not be checked-in, since it prevents all the tests from running.',
17+
},
18+
},
19+
20+
create (context) {
21+
return {
22+
Program (ast) {
23+
for (const testRun of utils.getTestInfo(context, ast)) {
24+
for (const test of [...testRun.valid, ...testRun.invalid]) {
25+
if (test.type === 'ObjectExpression') {
26+
// Test case object: { code: 'const x = 123;', ... }
27+
28+
const onlyProperty = test.properties.find(
29+
property =>
30+
property.key.type === 'Identifier' &&
31+
property.key.name === 'only' &&
32+
property.value.type === 'Literal' &&
33+
property.value.value
34+
);
35+
36+
if (onlyProperty) {
37+
context.report({ node: onlyProperty, messageId: 'foundOnly' });
38+
}
39+
} else if (
40+
test.type === 'CallExpression' &&
41+
test.callee.type === 'MemberExpression' &&
42+
test.callee.object.type === 'Identifier' &&
43+
test.callee.object.name === 'RuleTester' &&
44+
test.callee.property.type === 'Identifier' &&
45+
test.callee.property.name === 'only'
46+
) {
47+
// RuleTester.only('const x = 123;');
48+
context.report({ node: test.callee, messageId: 'foundOnly' });
49+
}
50+
}
51+
}
52+
},
53+
};
54+
},
55+
};

tests/lib/rules/no-only-tests.js

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
'use strict';
2+
3+
// ------------------------------------------------------------------------------
4+
// Requirements
5+
// ------------------------------------------------------------------------------
6+
7+
const rule = require('../../../lib/rules/no-only-tests');
8+
const RuleTester = require('eslint').RuleTester;
9+
10+
// ------------------------------------------------------------------------------
11+
// Tests
12+
// ------------------------------------------------------------------------------
13+
14+
const ruleTester = new RuleTester();
15+
ruleTester.run('no-only-tests', rule, {
16+
valid: [
17+
// No test cases with `only`
18+
`
19+
new RuleTester().run('foo', bar, {
20+
valid: [
21+
'foo',
22+
{ code: 'foo', foo: true },
23+
RuleTester.somethingElse(),
24+
notRuleTester.only()
25+
],
26+
invalid: [
27+
{ code: 'bar', foo: true },
28+
]
29+
});
30+
`,
31+
// `only` set to `false`
32+
`
33+
new RuleTester().run('foo', bar, {
34+
valid: [
35+
{ code: 'foo', only: false },
36+
],
37+
invalid: [
38+
{ code: 'bar', only: false },
39+
]
40+
});
41+
`,
42+
],
43+
44+
invalid: [
45+
{
46+
// Valid test case with `only`
47+
code: `
48+
new RuleTester().run('foo', bar, {
49+
valid: [
50+
{ code: 'foo', only: true },
51+
],
52+
invalid: []
53+
});
54+
`,
55+
output: null,
56+
errors: [{ messageId: 'foundOnly', type: 'Property', line: 4, endLine: 4, column: 28, endColumn: 38 }],
57+
},
58+
{
59+
// Valid test case using `RuleTester.only`
60+
code: `
61+
new RuleTester().run('foo', bar, {
62+
valid: [
63+
RuleTester.only('foo'),
64+
],
65+
invalid: []
66+
});
67+
`,
68+
output: null,
69+
errors: [{ messageId: 'foundOnly', type: 'MemberExpression', line: 4, endLine: 4, column: 13, endColumn: 28 }],
70+
},
71+
{
72+
// Invalid test case with `only`
73+
code: `
74+
new RuleTester().run('foo', bar, {
75+
valid: [],
76+
invalid: [
77+
{ code: 'foo', only: true },
78+
]
79+
});
80+
`,
81+
output: null,
82+
errors: [{ messageId: 'foundOnly', type: 'Property', line: 5, endLine: 5, column: 28, endColumn: 38 }],
83+
},
84+
],
85+
});

0 commit comments

Comments
 (0)