diff --git a/README.md b/README.md index def7a889c..4722c212e 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ Then configure the rules you want to use under the rules section. "jest/no-disabled-tests": "warn", "jest/no-focused-tests": "error", "jest/no-identical-title": "error", + "jest/prefer-to-be-defined": "warn", "jest/prefer-to-have-length": "warn", "jest/valid-expect": "error" } @@ -55,6 +56,8 @@ You can also whitelist the environment variables provided by Jest by doing: * [no-focused-tests](/docs/rules/no-focused-tests.md) - disallow focused tests. * [no-identical-title](/docs/rules/no-identical-title.md) - disallow identical titles. +* [prefer-to-be-defined](/docs/rules/prefer-to-be-defined.md) - suggest using + `toBeDefined()`. * [prefer-to-have-length](/docs/rules/prefer-to-have-length.md) - suggest using `toHaveLength()`. * [valid-expect](/docs/rules/valid-expect.md) - ensure expect is called @@ -85,6 +88,7 @@ The rules enabled in this configuration are: * [jest/no-disabled-tests](/docs/rules/no-disabled-tests.md) * [jest/no-focused-tests](/docs/rules/no-focused-tests.md) * [jest/no-identical-title](/docs/rules/no-identical-title.md) +* [jest/prefer-to-be-defined](/docs/rules/prefer-to-be-defined.md) * [jest/prefer-to-have-length](/docs/rules/prefer-to-have-length.md) * [jest/valid-expect](/docs/rules/valid-expect.md) diff --git a/docs/rules/prefer-to-be-defined.md b/docs/rules/prefer-to-be-defined.md new file mode 100644 index 000000000..7a922e58b --- /dev/null +++ b/docs/rules/prefer-to-be-defined.md @@ -0,0 +1,28 @@ +# Suggest using `toBeDefined()` (prefer-to-be-defined) + +In order to have a better failure message, `toBeDefined()` should be used upon asserting expections on defined value. + +## Rule details + +This rule triggers a warning if `toBe()` is used to assert a undefined value. + +```js +expect(true).not.toBe(undefined); +``` + +This rule is enabled by default. + +### Default configuration + +The following patterns are considered warning: + +```js +expect(true).not.toBe(undefined); +expect(true).not.toBeUndefined(); +``` + +The following pattern is not warning: + +```js +expect(true).toBeDefined(); +``` diff --git a/index.js b/index.js index 9928e7ba1..bdb7ae8c4 100644 --- a/index.js +++ b/index.js @@ -3,6 +3,7 @@ const noDisabledTests = require('./rules/no_disabled_tests'); const noFocusedTests = require('./rules/no_focused_tests'); const noIdenticalTitle = require('./rules/no_identical_title'); +const preferToBeDefined = require('./rules/prefer_to_be_defined'); const preferToHaveLength = require('./rules/prefer_to_have_length'); const validExpect = require('./rules/valid_expect'); @@ -13,6 +14,7 @@ module.exports = { 'jest/no-disabled-tests': 'warn', 'jest/no-focused-tests': 'error', 'jest/no-identical-title': 'error', + 'jest/prefer-to-be-defined': 'warn', 'jest/prefer-to-have-length': 'warn', 'jest/valid-expect': 'error', }, @@ -45,6 +47,7 @@ module.exports = { 'no-disabled-tests': noDisabledTests, 'no-focused-tests': noFocusedTests, 'no-identical-title': noIdenticalTitle, + 'prefer-to-be-defined': preferToBeDefined, 'prefer-to-have-length': preferToHaveLength, 'valid-expect': validExpect, }, diff --git a/rules/__tests__/prefer_to_be_defined.test.js b/rules/__tests__/prefer_to_be_defined.test.js new file mode 100644 index 000000000..dfdeaed0b --- /dev/null +++ b/rules/__tests__/prefer_to_be_defined.test.js @@ -0,0 +1,35 @@ +'use strict'; + +const RuleTester = require('eslint').RuleTester; +const { rules } = require('../../'); + +const ruleTester = new RuleTester(); + +ruleTester.run('prefer_to_be_defined', rules['prefer-to-be-defined'], { + valid: ['expect(true).toBeDefined();'], + + invalid: [ + { + code: 'expect(true).not.toBe(undefined);', + errors: [ + { + message: 'Use toBeDefined() instead', + column: 14, + line: 1, + }, + ], + output: 'expect(true).toBeDefined();', + }, + { + code: 'expect(true).not.toBeUndefined();', + errors: [ + { + message: 'Use toBeDefined() instead', + column: 14, + line: 1, + }, + ], + output: 'expect(true).toBeDefined();', + }, + ], +}); diff --git a/rules/prefer_to_be_defined.js b/rules/prefer_to_be_defined.js new file mode 100644 index 000000000..e954f464f --- /dev/null +++ b/rules/prefer_to_be_defined.js @@ -0,0 +1,53 @@ +'use strict'; + +module.exports = context => { + return { + CallExpression(node) { + const calleeName = node.callee.name; + + if ( + calleeName === 'expect' && + node.arguments.length == 1 && + node.parent && + node.parent.type === 'MemberExpression' && + node.parent.parent && + node.parent.parent.type === 'MemberExpression' + ) { + const parentProperty = node.parent.property; + const propertyName = parentProperty.name; + const parentProperty2 = node.parent.parent.property; + const propertyName2 = parentProperty2.name; + const argument = node.parent.parent.parent.arguments[0]; + const propertyDot = context + .getSourceCode() + .getFirstTokenBetween( + parentProperty, + parentProperty2, + token => token.value === '.' + ); + if ( + (propertyName === 'not' && + propertyName2 === 'toBe' && + argument.value === undefined) || + (propertyName === 'not' && propertyName2 === 'toBeUndefined') + ) { + context.report({ + fix(fixer) { + const fixes = [ + fixer.remove(parentProperty), + fixer.remove(propertyDot), + fixer.replaceText(parentProperty2, 'toBeDefined'), + ]; + if (argument) { + fixes.push(fixer.remove(argument)); + } + return fixes; + }, + message: 'Use toBeDefined() instead', + node: parentProperty, + }); + } + } + }, + }; +};