From 91917677fac71f34e92e4a9b663563be9f6c7912 Mon Sep 17 00:00:00 2001 From: osama Date: Thu, 13 Oct 2022 01:34:05 +0200 Subject: [PATCH] feat(prefer-tobe): implement rules for preferences for true/false/null --- README.md | 6 ++ docs/rules/prefer-toBeFalse.md | 51 ++++++++++++++++ docs/rules/prefer-toBeNull.md | 51 ++++++++++++++++ docs/rules/prefer-toBeTrue.md | 51 ++++++++++++++++ index.js | 5 +- lib/rules/prefer-toBeFalse.js | 55 +++++++++++++++++ lib/rules/prefer-toBeNull.js | 55 +++++++++++++++++ lib/rules/prefer-toBeTrue.js | 55 +++++++++++++++++ test/rules/prefer-toBeFalse.js | 107 +++++++++++++++++++++++++++++++++ test/rules/prefer-toBeNull.js | 107 +++++++++++++++++++++++++++++++++ test/rules/prefer-toBeTrue.js | 107 +++++++++++++++++++++++++++++++++ 11 files changed, 649 insertions(+), 1 deletion(-) create mode 100644 docs/rules/prefer-toBeFalse.md create mode 100644 docs/rules/prefer-toBeNull.md create mode 100644 docs/rules/prefer-toBeTrue.md create mode 100644 lib/rules/prefer-toBeFalse.js create mode 100644 lib/rules/prefer-toBeNull.js create mode 100644 lib/rules/prefer-toBeTrue.js create mode 100644 test/rules/prefer-toBeFalse.js create mode 100644 test/rules/prefer-toBeNull.js create mode 100644 test/rules/prefer-toBeTrue.js diff --git a/README.md b/README.md index 3d265bd..c038b03 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,9 @@ Rule | Recommended | Options [prefer-promise-strategies][] | 1 | [prefer-toHaveBeenCalledWith][] | 1 | [prefer-toBeUndefined][] | 0 | `['always', 'never']` +[prefer-toBeNull][] | 0 | `['always', 'never']` +[prefer-toBeTrue][] | 0 | `['always', 'never']` +[prefer-toBeFalse][] | 0 | `['always', 'never']` For example, using the recommended configuration, the `no-focused-tests` rule @@ -135,6 +138,9 @@ See [configuring rules][] for more information. [prefer-promise-strategies]: docs/rules/prefer-promise-strategies.md [prefer-toHaveBeenCalledWith]: docs/rules/prefer-toHaveBeenCalledWith.md [prefer-toBeUndefined]: docs/rules/prefer-toBeUndefined.md +[prefer-toBeNull]: docs/rules/prefer-toBeNull.md +[prefer-toBeTrue]: docs/rules/prefer-toBeTrue.md +[prefer-toBeFalse]: docs/rules/prefer-toBeFalse.md [configuring rules]: http://eslint.org/docs/user-guide/configuring#configuring-rules diff --git a/docs/rules/prefer-toBeFalse.md b/docs/rules/prefer-toBeFalse.md new file mode 100644 index 0000000..c3b1a7d --- /dev/null +++ b/docs/rules/prefer-toBeFalse.md @@ -0,0 +1,51 @@ +# Prefer toBeFalse + +This rule recommends using `toBeFalse` instead of the more generic matcher +`toBe(false)`. + +## Rule details + +This rule forces a codebase to be consistent when expecting values to be +`false` in unit tests. + +## Options + +### always + +The `"always"` option (default) prefers `toBeFalse()`. Select this option +if you'd like developers to use a matcher that tests specifically for +`false`. + +Examples of *incorrect* code for the `"always"` option: + +```js +expect(value).toBe(false); +expect(value).toBe(false, 'with an explanation'); +``` + +Examples of *correct* code for the `"always"` option: + +```js +expect(value).toBeFalse(); +expect(value).toBeFalse('with an explanation'); +``` + +### never + +The `"never"` option prefers `toBe(false)`. Select this option if you'd +like developers to use the `toBe` matcher consistently, whether testing +for `false` or not. + +Examples of *incorrect* code for the `"never"` option: + +```js +expect(value).toBeFalse(); +expect(value).toBeFalse('with an explanation'); +``` + +Examples of *correct* code for the `"never"` option: + +```js +expect(value).toBe(false); +expect(value).toBe(false, 'with an explanation'); +``` diff --git a/docs/rules/prefer-toBeNull.md b/docs/rules/prefer-toBeNull.md new file mode 100644 index 0000000..b56a374 --- /dev/null +++ b/docs/rules/prefer-toBeNull.md @@ -0,0 +1,51 @@ +# Prefer toBeNull + +This rule recommends using `toBeNull` instead of the more generic matcher +`toBe(null)`. + +## Rule details + +This rule forces a codebase to be consistent when expecting values to be +`null` in unit tests. + +## Options + +### always + +The `"always"` option (default) prefers `toBeNull()`. Select this option +if you'd like developers to use a matcher that tests specifically for +`null`. + +Examples of *incorrect* code for the `"always"` option: + +```js +expect(value).toBe(null); +expect(value).toBe(null, 'with an explanation'); +``` + +Examples of *correct* code for the `"always"` option: + +```js +expect(value).toBeNull(); +expect(value).toBeNull('with an explanation'); +``` + +### never + +The `"never"` option prefers `toBe(null)`. Select this option if you'd +like developers to use the `toBe` matcher consistently, whether testing +for `null` or not. + +Examples of *incorrect* code for the `"never"` option: + +```js +expect(value).toBeNull(); +expect(value).toBeNull('with an explanation'); +``` + +Examples of *correct* code for the `"never"` option: + +```js +expect(value).toBe(null); +expect(value).toBe(null, 'with an explanation'); +``` diff --git a/docs/rules/prefer-toBeTrue.md b/docs/rules/prefer-toBeTrue.md new file mode 100644 index 0000000..b920e2d --- /dev/null +++ b/docs/rules/prefer-toBeTrue.md @@ -0,0 +1,51 @@ +# Prefer toBeTrue + +This rule recommends using `toBeTrue` instead of the more generic matcher +`toBe(true)`. + +## Rule details + +This rule forces a codebase to be consistent when expecting values to be +`true` in unit tests. + +## Options + +### always + +The `"always"` option (default) prefers `toBeTrue()`. Select this option +if you'd like developers to use a matcher that tests specifically for +`true`. + +Examples of *incorrect* code for the `"always"` option: + +```js +expect(value).toBe(true); +expect(value).toBe(true, 'with an explanation'); +``` + +Examples of *correct* code for the `"always"` option: + +```js +expect(value).toBeTrue(); +expect(value).toBeTrue('with an explanation'); +``` + +### never + +The `"never"` option prefers `toBe(true)`. Select this option if you'd +like developers to use the `toBe` matcher consistently, whether testing +for `true` or not. + +Examples of *incorrect* code for the `"never"` option: + +```js +expect(value).toBeTrue(); +expect(value).toBeTrue('with an explanation'); +``` + +Examples of *correct* code for the `"never"` option: + +```js +expect(value).toBe(true); +expect(value).toBe(true, 'with an explanation'); +``` diff --git a/index.js b/index.js index f79cc89..2a012e4 100644 --- a/index.js +++ b/index.js @@ -24,7 +24,10 @@ module.exports = { 'prefer-jasmine-matcher': require('./lib/rules/prefer-jasmine-matcher'), 'prefer-promise-strategies': require('./lib/rules/prefer-promise-strategies'), 'prefer-toHaveBeenCalledWith': require('./lib/rules/prefer-toHaveBeenCalledWith'), - 'prefer-toBeUndefined': require('./lib/rules/prefer-toBeUndefined') + 'prefer-toBeUndefined': require('./lib/rules/prefer-toBeUndefined'), + 'prefer-toBeNull': require('./lib/rules/prefer-toBeNull'), + 'prefer-toBeTrue': require('./lib/rules/prefer-toBeTrue'), + 'prefer-toBeFalse': require('./lib/rules/prefer-toBeFalse') }, configs: { recommended: { diff --git a/lib/rules/prefer-toBeFalse.js b/lib/rules/prefer-toBeFalse.js new file mode 100644 index 0000000..05ae539 --- /dev/null +++ b/lib/rules/prefer-toBeFalse.js @@ -0,0 +1,55 @@ +'use strict' + +/** + * @fileoverview Prefer toBeTrue instead of toBe(true) + * @author Osama Yousry + */ + +module.exports = { + meta: { + schema: [ + { + enum: ['always', 'never'] + } + ], + fixable: 'code' + }, + create: function (context) { + const always = context.options[0] !== 'never' + + return { + CallExpression: function (node) { + if (always) { + if (node.callee.type === 'MemberExpression' && node.callee.property.name === 'toBe' && + node.arguments[0] && node.arguments[0].type === 'Literal' && node.arguments[0].value === false) { + context.report({ + message: 'Prefer toBeFalse() to expect false', + node: node.callee.property, + fix: function (fixer) { + if (node.arguments.length === 1) { + return fixer.replaceTextRange([node.callee.property.start, node.arguments[0].end], 'toBeFalse(') + } else { + return fixer.replaceTextRange([node.callee.property.start, node.arguments[1].start], 'toBeFalse(') + } + } + }) + } + } else { + if (node.callee.type === 'MemberExpression' && node.callee.property.name === 'toBeFalse') { + context.report({ + message: 'Prefer toBe(false) to expect false', + node: node.callee.property, + fix: function (fixer) { + if (node.arguments.length === 0) { + return fixer.replaceTextRange([node.callee.property.start, node.end], 'toBe(false)') + } else { + return fixer.replaceTextRange([node.callee.property.start, node.arguments[0].start], 'toBe(false, ') + } + } + }) + } + } + } + } + } +} diff --git a/lib/rules/prefer-toBeNull.js b/lib/rules/prefer-toBeNull.js new file mode 100644 index 0000000..50e0240 --- /dev/null +++ b/lib/rules/prefer-toBeNull.js @@ -0,0 +1,55 @@ +'use strict' + +/** + * @fileoverview Prefer toBeNull instead of toBe(null) + * @author Osama Yousry + */ + +module.exports = { + meta: { + schema: [ + { + enum: ['always', 'never'] + } + ], + fixable: 'code' + }, + create: function (context) { + const always = context.options[0] !== 'never' + + return { + CallExpression: function (node) { + if (always) { + if (node.callee.type === 'MemberExpression' && node.callee.property.name === 'toBe' && + node.arguments[0] && node.arguments[0].type === 'Literal' && node.arguments[0].value === null) { + context.report({ + message: 'Prefer toBeNull() to expect null', + node: node.callee.property, + fix: function (fixer) { + if (node.arguments.length === 1) { + return fixer.replaceTextRange([node.callee.property.start, node.arguments[0].end], 'toBeNull(') + } else { + return fixer.replaceTextRange([node.callee.property.start, node.arguments[1].start], 'toBeNull(') + } + } + }) + } + } else { + if (node.callee.type === 'MemberExpression' && node.callee.property.name === 'toBeNull') { + context.report({ + message: 'Prefer toBe(null) to expect null', + node: node.callee.property, + fix: function (fixer) { + if (node.arguments.length === 0) { + return fixer.replaceTextRange([node.callee.property.start, node.end], 'toBe(null)') + } else { + return fixer.replaceTextRange([node.callee.property.start, node.arguments[0].start], 'toBe(null, ') + } + } + }) + } + } + } + } + } +} diff --git a/lib/rules/prefer-toBeTrue.js b/lib/rules/prefer-toBeTrue.js new file mode 100644 index 0000000..b886c99 --- /dev/null +++ b/lib/rules/prefer-toBeTrue.js @@ -0,0 +1,55 @@ +'use strict' + +/** + * @fileoverview Prefer toBeTrue instead of toBe(true) + * @author Osama Yousry + */ + +module.exports = { + meta: { + schema: [ + { + enum: ['always', 'never'] + } + ], + fixable: 'code' + }, + create: function (context) { + const always = context.options[0] !== 'never' + + return { + CallExpression: function (node) { + if (always) { + if (node.callee.type === 'MemberExpression' && node.callee.property.name === 'toBe' && + node.arguments[0] && node.arguments[0].type === 'Literal' && node.arguments[0].value === true) { + context.report({ + message: 'Prefer toBeTrue() to expect true', + node: node.callee.property, + fix: function (fixer) { + if (node.arguments.length === 1) { + return fixer.replaceTextRange([node.callee.property.start, node.arguments[0].end], 'toBeTrue(') + } else { + return fixer.replaceTextRange([node.callee.property.start, node.arguments[1].start], 'toBeTrue(') + } + } + }) + } + } else { + if (node.callee.type === 'MemberExpression' && node.callee.property.name === 'toBeTrue') { + context.report({ + message: 'Prefer toBe(true) to expect true', + node: node.callee.property, + fix: function (fixer) { + if (node.arguments.length === 0) { + return fixer.replaceTextRange([node.callee.property.start, node.end], 'toBe(true)') + } else { + return fixer.replaceTextRange([node.callee.property.start, node.arguments[0].start], 'toBe(true, ') + } + } + }) + } + } + } + } + } +} diff --git a/test/rules/prefer-toBeFalse.js b/test/rules/prefer-toBeFalse.js new file mode 100644 index 0000000..eb044d6 --- /dev/null +++ b/test/rules/prefer-toBeFalse.js @@ -0,0 +1,107 @@ +'use strict' + +var rule = require('../../lib/rules/prefer-toBeFalse') +var linesToCode = require('../helpers/lines_to_code') +var RuleTester = require('eslint').RuleTester + +var eslintTester = new RuleTester() + +eslintTester.run('prefer toBeFalse', rule, { + valid: [ + { + code: linesToCode([ + 'expect(x).toBeFalse();' + ]) + }, + { + code: linesToCode([ + 'expect(x).toBeFalse("with an optional message");' + ]) + }, + { + options: ['never'], + code: linesToCode([ + 'expect(x).toBe(false);' + ]) + }, + { + options: ['never'], + code: linesToCode([ + 'expect(x).toBe(false, "with an optional message");' + ]) + } + ], + invalid: [ + { + code: linesToCode([ + 'expect(x).toBe(false);' + ]), + output: linesToCode([ + 'expect(x).toBeFalse();' + ]), + errors: [ + { + message: 'Prefer toBeFalse() to expect false' + } + ] + }, + { + code: linesToCode([ + 'expect(x).toBe(false, "with an optional message", "???");' + ]), + output: linesToCode([ + 'expect(x).toBeFalse("with an optional message", "???");' + ]), + errors: [ + { + message: 'Prefer toBeFalse() to expect false' + } + ] + }, + { + code: linesToCode([ + 'expect(x).toBe(', + ' false,', + ' "why would you do this?"', + ');' + ]), + output: linesToCode([ + 'expect(x).toBeFalse("why would you do this?"', + ');' + ]), + errors: [ + { + message: 'Prefer toBeFalse() to expect false' + } + ] + }, + { + options: ['never'], + code: linesToCode([ + 'expect(x).toBeFalse();' + ]), + output: linesToCode([ + 'expect(x).toBe(false);' + ]), + errors: [ + { + message: 'Prefer toBe(false) to expect false' + } + ] + }, + { + options: ['never'], + code: linesToCode([ + 'expect(x).toBeFalse("with optional args", "???");' + ]), + output: linesToCode([ + 'expect(x).toBe(false, "with optional args", "???");' + ]), + errors: [ + { + message: 'Prefer toBe(false) to expect false' + } + ] + } + ] +}) diff --git a/test/rules/prefer-toBeNull.js b/test/rules/prefer-toBeNull.js new file mode 100644 index 0000000..c9cdddc --- /dev/null +++ b/test/rules/prefer-toBeNull.js @@ -0,0 +1,107 @@ +'use strict' + +var rule = require('../../lib/rules/prefer-toBeNull') +var linesToCode = require('../helpers/lines_to_code') +var RuleTester = require('eslint').RuleTester + +var eslintTester = new RuleTester() + +eslintTester.run('prefer toBeNull', rule, { + valid: [ + { + code: linesToCode([ + 'expect(x).toBeNull();' + ]) + }, + { + code: linesToCode([ + 'expect(x).toBeNull("with an optional message");' + ]) + }, + { + options: ['never'], + code: linesToCode([ + 'expect(x).toBe(null);' + ]) + }, + { + options: ['never'], + code: linesToCode([ + 'expect(x).toBe(null, "with an optional message");' + ]) + } + ], + invalid: [ + { + code: linesToCode([ + 'expect(x).toBe(null);' + ]), + output: linesToCode([ + 'expect(x).toBeNull();' + ]), + errors: [ + { + message: 'Prefer toBeNull() to expect null' + } + ] + }, + { + code: linesToCode([ + 'expect(x).toBe(null, "with an optional message", "???");' + ]), + output: linesToCode([ + 'expect(x).toBeNull("with an optional message", "???");' + ]), + errors: [ + { + message: 'Prefer toBeNull() to expect null' + } + ] + }, + { + code: linesToCode([ + 'expect(x).toBe(', + ' null,', + ' "why would you do this?"', + ');' + ]), + output: linesToCode([ + 'expect(x).toBeNull("why would you do this?"', + ');' + ]), + errors: [ + { + message: 'Prefer toBeNull() to expect null' + } + ] + }, + { + options: ['never'], + code: linesToCode([ + 'expect(x).toBeNull();' + ]), + output: linesToCode([ + 'expect(x).toBe(null);' + ]), + errors: [ + { + message: 'Prefer toBe(null) to expect null' + } + ] + }, + { + options: ['never'], + code: linesToCode([ + 'expect(x).toBeNull("with optional args", "???");' + ]), + output: linesToCode([ + 'expect(x).toBe(null, "with optional args", "???");' + ]), + errors: [ + { + message: 'Prefer toBe(null) to expect null' + } + ] + } + ] +}) diff --git a/test/rules/prefer-toBeTrue.js b/test/rules/prefer-toBeTrue.js new file mode 100644 index 0000000..139ebf7 --- /dev/null +++ b/test/rules/prefer-toBeTrue.js @@ -0,0 +1,107 @@ +'use strict' + +var rule = require('../../lib/rules/prefer-toBeTrue') +var linesToCode = require('../helpers/lines_to_code') +var RuleTester = require('eslint').RuleTester + +var eslintTester = new RuleTester() + +eslintTester.run('prefer toBeTrue', rule, { + valid: [ + { + code: linesToCode([ + 'expect(x).toBeTrue();' + ]) + }, + { + code: linesToCode([ + 'expect(x).toBeTrue("with an optional message");' + ]) + }, + { + options: ['never'], + code: linesToCode([ + 'expect(x).toBe(true);' + ]) + }, + { + options: ['never'], + code: linesToCode([ + 'expect(x).toBe(true, "with an optional message");' + ]) + } + ], + invalid: [ + { + code: linesToCode([ + 'expect(x).toBe(true);' + ]), + output: linesToCode([ + 'expect(x).toBeTrue();' + ]), + errors: [ + { + message: 'Prefer toBeTrue() to expect true' + } + ] + }, + { + code: linesToCode([ + 'expect(x).toBe(true, "with an optional message", "???");' + ]), + output: linesToCode([ + 'expect(x).toBeTrue("with an optional message", "???");' + ]), + errors: [ + { + message: 'Prefer toBeTrue() to expect true' + } + ] + }, + { + code: linesToCode([ + 'expect(x).toBe(', + ' true,', + ' "why would you do this?"', + ');' + ]), + output: linesToCode([ + 'expect(x).toBeTrue("why would you do this?"', + ');' + ]), + errors: [ + { + message: 'Prefer toBeTrue() to expect true' + } + ] + }, + { + options: ['never'], + code: linesToCode([ + 'expect(x).toBeTrue();' + ]), + output: linesToCode([ + 'expect(x).toBe(true);' + ]), + errors: [ + { + message: 'Prefer toBe(true) to expect true' + } + ] + }, + { + options: ['never'], + code: linesToCode([ + 'expect(x).toBeTrue("with optional args", "???");' + ]), + output: linesToCode([ + 'expect(x).toBe(true, "with optional args", "???");' + ]), + errors: [ + { + message: 'Prefer toBe(true) to expect true' + } + ] + } + ] +})