Skip to content

Commit

Permalink
New: add new rule no-only-tests (#145)
Browse files Browse the repository at this point in the history
  • Loading branch information
bmish authored Jun 24, 2021
1 parent 6762a3f commit f0ac31c
Show file tree
Hide file tree
Showing 4 changed files with 419 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
55 changes: 55 additions & 0 deletions docs/rules/no-only-tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# 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 */

const { RuleTester } = require('eslint');
const ruleTester = new RuleTester();

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 */

const { RuleTester } = require('eslint');
const ruleTester = new RuleTester();

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

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

/**
* Checks if the given token is a comma token or not.
* From: https://github.com/eslint/eslint/blob/master/lib/rules/utils/ast-utils.js
* @param {Token} token The token to check.
* @returns {boolean} `true` if the token is a comma token.
*/
function isCommaToken (token) {
return token.value === ',' && token.type === 'Punctuator';
}

/**
* Checks if the given token is an opening brace token or not.
* From: https://github.com/eslint/eslint/blob/master/lib/rules/utils/ast-utils.js
* @param {Token} token The token to check.
* @returns {boolean} `true` if the token is an opening brace token.
*/
function isOpeningBraceToken (token) {
return token.value === '{' && token.type === 'Punctuator';
}

/**
* Checks if the given token is a closing brace token or not.
* From: https://github.com/eslint/eslint/blob/master/lib/rules/utils/ast-utils.js
* @param {Token} token The token to check.
* @returns {boolean} `true` if the token is a closing brace token.
*/
function isClosingBraceToken (token) {
return token.value === '}' && token.type === 'Punctuator';
}

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.',
removeOnly: 'Remove `only`.',
},
hasSuggestions: true,
},

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',
suggest: [
{
messageId: 'removeOnly',
*fix (fixer) {
const sourceCode = context.getSourceCode();

const tokenBefore = sourceCode.getTokenBefore(onlyProperty);
const tokenAfter = sourceCode.getTokenAfter(onlyProperty);
if (
(isCommaToken(tokenBefore) && isCommaToken(tokenAfter)) || // In middle of properties
(isOpeningBraceToken(tokenBefore) && isCommaToken(tokenAfter)) // At beginning of properties
) {
yield fixer.remove(tokenAfter); // Remove extra comma.
}
if (isCommaToken(tokenBefore) && isClosingBraceToken(tokenAfter)) { // At end of properties
yield fixer.remove(tokenBefore); // Remove extra comma.
}

yield fixer.remove(onlyProperty);
},
},
],
});
}
} 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' });
}
}
}
},
};
},
};
Loading

0 comments on commit f0ac31c

Please sign in to comment.