Skip to content

Commit

Permalink
Add prefer-to-have-length rule
Browse files Browse the repository at this point in the history
  • Loading branch information
xfumihiro committed Oct 29, 2017
1 parent cfbda80 commit c6ca332
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* `[jest-config]` Include error message for `preset` json ([#4766](https://github.com/facebook/jest/pull/4766))

### Features
* `[eslint-plugin-jest]` Add `prefer-to-have-length` lint rule. ([#4771](https://github.com/facebook/jest/pull/4771))
* `[jest-environment-jsdom]` [**BREAKING**] Upgrade to JSDOM@11 ([#4770](https://github.com/facebook/jest/pull/4770))
* `[jest-environment-*]` [**BREAKING**] Add Async Test Environment APIs, dispose is now teardown ([#4506](https://github.com/facebook/jest/pull/4506))
* `[jest-cli]` Add an option to clear the cache ([#4430](https://github.com/facebook/jest/pull/4430))
Expand Down
27 changes: 27 additions & 0 deletions packages/eslint-plugin-jest/docs/rules/prefer-to-have-length.md
Original file line number Diff line number Diff line change
@@ -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);
```
3 changes: 3 additions & 0 deletions packages/eslint-plugin-jest/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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',
},
},
Expand Down Expand Up @@ -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,
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* 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,
},
],
output: 'expect(files).toHaveLength(1);',
},
],
});
53 changes: 53 additions & 0 deletions packages/eslint-plugin-jest/src/rules/prefer_to_have_length.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* 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;
const argumentObject = node.arguments[0].object;
const argumentProperty = node.arguments[0].property;

if (propertyName === 'toBe' && argumentProperty.name === 'length') {
// $FlowFixMe
const propertyDot = context
.getSourceCode()
.getFirstTokenBetween(
argumentObject,
argumentProperty,
token => token.value === '.',
);
context.report({
fix(fixer) {
return [
fixer.remove(propertyDot),
fixer.remove(argumentProperty),
fixer.replaceText(parentProperty, 'toHaveLength'),
];
},
message: 'Use toHaveLength() instead',
node: parentProperty,
});
}
}
},
};
};
2 changes: 2 additions & 0 deletions packages/eslint-plugin-jest/src/rules/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export type Literal = {
value?: string,
rawValue?: string,
parent: ParentNode,
property: Identifier,
object: Identifier,
loc: NodeLocation,
};

Expand Down

0 comments on commit c6ca332

Please sign in to comment.