Skip to content

Commit

Permalink
feat: add new rule no-only-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
bmish committed Jun 24, 2021
1 parent 65cfb2c commit 83bb4bb
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Name | ✔️ | 🛠 | Description
[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
[no-identical-tests](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/no-identical-tests.md) | ✔️ | 🛠 | disallow identical tests
[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
[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`
[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
[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
[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.
Expand Down
49 changes: 49 additions & 0 deletions docs/rules/no-only-tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Disallow the test case property `only` (no-only-tests)

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.

## Rule Details

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.

Examples of **incorrect** code for this rule:

```js
/* eslint eslint-plugin/no-only-tests: error */

ruleTester.run('my-rule', myRule, {
valid: [
{
code: 'const valid = 42;',
only: true,
},
RuleTester.only('const valid = 42;'),
],
invalid: [
{
code: 'const invalid = 42;',
only: true,
errors: [/* ... */],
},
],
});
```

Examples of **correct** code for this rule:

```js
/* eslint eslint-plugin/no-only-tests: error */

ruleTester.run('my-rule', myRule, {
valid: [
'const valid = 42;',
{ code: 'const valid = 42;' }
],
invalid: [
{
code: 'const invalid = 42;',
errors: [/* ... */],
},
],
});
```
55 changes: 55 additions & 0 deletions lib/rules/no-only-tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use strict';

const utils = require('../utils');

module.exports = {
meta: {
type: 'problem',
docs: {
description: 'disallow the test case property `only`',
category: 'Tests',
recommended: false,
},
schema: [],
messages: {
foundOnly:
'The test case property `only` can be used during development, but should not be checked-in, since it prevents all the tests from running.',
},
},

create (context) {
return {
Program (ast) {
for (const testRun of utils.getTestInfo(context, ast)) {
for (const test of [...testRun.valid, ...testRun.invalid]) {
if (test.type === 'ObjectExpression') {
// Test case object: { code: 'const x = 123;', ... }

const onlyProperty = test.properties.find(
property =>
property.key.type === 'Identifier' &&
property.key.name === 'only' &&
property.value.type === 'Literal' &&
property.value.value
);

if (onlyProperty) {
context.report({ node: onlyProperty, messageId: 'foundOnly' });
}
} else if (
test.type === 'CallExpression' &&
test.callee.type === 'MemberExpression' &&
test.callee.object.type === 'Identifier' &&
test.callee.object.name === 'RuleTester' &&
test.callee.property.type === 'Identifier' &&
test.callee.property.name === 'only'
) {
// RuleTester.only('const x = 123;');
context.report({ node: test.callee, messageId: 'foundOnly' });
}
}
}
},
};
},
};
85 changes: 85 additions & 0 deletions tests/lib/rules/no-only-tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
'use strict';

// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

const rule = require('../../../lib/rules/no-only-tests');
const RuleTester = require('eslint').RuleTester;

// ------------------------------------------------------------------------------
// Tests
// ------------------------------------------------------------------------------

const ruleTester = new RuleTester();
ruleTester.run('no-only-tests', rule, {
valid: [
// No test cases with `only`
`
new RuleTester().run('foo', bar, {
valid: [
'foo',
{ code: 'foo', foo: true },
RuleTester.somethingElse(),
notRuleTester.only()
],
invalid: [
{ code: 'bar', foo: true },
]
});
`,
// `only` set to `false`
`
new RuleTester().run('foo', bar, {
valid: [
{ code: 'foo', only: false },
],
invalid: [
{ code: 'bar', only: false },
]
});
`,
],

invalid: [
{
// Valid test case with `only`
code: `
new RuleTester().run('foo', bar, {
valid: [
{ code: 'foo', only: true },
],
invalid: []
});
`,
output: null,
errors: [{ messageId: 'foundOnly', type: 'Property', line: 4, endLine: 4, column: 28, endColumn: 38 }],
},
{
// Valid test case using `RuleTester.only`
code: `
new RuleTester().run('foo', bar, {
valid: [
RuleTester.only('foo'),
],
invalid: []
});
`,
output: null,
errors: [{ messageId: 'foundOnly', type: 'MemberExpression', line: 4, endLine: 4, column: 13, endColumn: 28 }],
},
{
// Invalid test case with `only`
code: `
new RuleTester().run('foo', bar, {
valid: [],
invalid: [
{ code: 'foo', only: true },
]
});
`,
output: null,
errors: [{ messageId: 'foundOnly', type: 'Property', line: 5, endLine: 5, column: 28, endColumn: 38 }],
},
],
});

0 comments on commit 83bb4bb

Please sign in to comment.