diff --git a/packages/eslint-plugin-jest/docs/rules/prefer-to-have-length.md b/packages/eslint-plugin-jest/docs/rules/prefer-to-have-length.md new file mode 100644 index 000000000000..ba56c065ea35 --- /dev/null +++ b/packages/eslint-plugin-jest/docs/rules/prefer-to-have-length.md @@ -0,0 +1,27 @@ +# Suggest using `toHaveLength()` (prefer-to-have-length) + +In order to have a better failure message, `toHaveLength()` should be used upon asserting expections on object's length property. + +## Rule details + +This rule triggers a warning if `toBe()` is used to assert object's length property. + +```js +expect(files.length).toBe(1); +``` + +This rule is enabled by default. + +### Default configuration + +The following pattern is considered warning: + +```js +expect(files.length).toBe(1); +``` + +The following pattern is not warning: + +```js +expect(files).toHaveLength(1); +``` diff --git a/packages/eslint-plugin-jest/src/index.js b/packages/eslint-plugin-jest/src/index.js index 18999cbfa8fd..83b270b3ca10 100644 --- a/packages/eslint-plugin-jest/src/index.js +++ b/packages/eslint-plugin-jest/src/index.js @@ -10,6 +10,7 @@ import noDisabledTests from './rules/no_disabled_tests'; import noFocusedTests from './rules/no_focused_tests'; import noIdenticalTitle from './rules/no_identical_title'; +import preferToHaveLength from './rules/prefer_to_have_length'; import validExpect from './rules/valid_expect'; module.exports = { @@ -19,6 +20,7 @@ module.exports = { 'jest/no-disabled-tests': 'warn', 'jest/no-focused-tests': 'error', 'jest/no-identical-title': 'error', + 'jest/prefer-to-have-length': 'warn', 'jest/valid-expect': 'error', }, }, @@ -50,6 +52,7 @@ module.exports = { 'no-disabled-tests': noDisabledTests, 'no-focused-tests': noFocusedTests, 'no-identical-title': noIdenticalTitle, + 'prefer-to-have-length': preferToHaveLength, 'valid-expect': validExpect, }, }; diff --git a/packages/eslint-plugin-jest/src/rules/__tests__/prefer_to_have_length.test.js b/packages/eslint-plugin-jest/src/rules/__tests__/prefer_to_have_length.test.js new file mode 100644 index 000000000000..4283429e78b4 --- /dev/null +++ b/packages/eslint-plugin-jest/src/rules/__tests__/prefer_to_have_length.test.js @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +/* eslint-disable sort-keys */ + +'use strict'; + +import {RuleTester} from 'eslint'; +const {rules} = require('../../'); + +const ruleTester = new RuleTester(); + +ruleTester.run('prefer_to_have_length', rules['prefer-to-have-length'], { + valid: ['expect(files).toHaveLength(1);', "expect(files.name).toBe('file');"], + + invalid: [ + { + code: 'expect(files.length).toBe(1);', + errors: [ + { + message: 'Use toHaveLength() instead', + column: 22, + line: 1, + }, + ], + }, + ], +}); diff --git a/packages/eslint-plugin-jest/src/rules/prefer_to_have_length.js b/packages/eslint-plugin-jest/src/rules/prefer_to_have_length.js new file mode 100644 index 000000000000..3e378bf24c83 --- /dev/null +++ b/packages/eslint-plugin-jest/src/rules/prefer_to_have_length.js @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import type {EslintContext, CallExpression} from './types'; + +export default (context: EslintContext) => { + return { + CallExpression(node: CallExpression) { + const calleeName = node.callee.name; + + if ( + calleeName === 'expect' && + node.arguments.length == 1 && + node.parent && + node.parent.type === 'MemberExpression' && + node.parent.parent + ) { + const parentProperty = node.parent.property; + const propertyName = parentProperty.name; + + if ( + propertyName === 'toBe' && + node.arguments[0].property.name === 'length' + ) { + context.report({ + message: 'Use toHaveLength() instead', + node: parentProperty, + }); + } + } + }, + }; +}; diff --git a/packages/eslint-plugin-jest/src/rules/types.js b/packages/eslint-plugin-jest/src/rules/types.js index ec2f39a8ef4b..834f487cc313 100644 --- a/packages/eslint-plugin-jest/src/rules/types.js +++ b/packages/eslint-plugin-jest/src/rules/types.js @@ -44,6 +44,7 @@ export type Literal = { value?: string, rawValue?: string, parent: ParentNode, + property: Identifier, loc: NodeLocation, };