Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create no-deprecated-functions #560

Merged
merged 4 commits into from May 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -115,6 +115,7 @@ installations requiring long-term consistency.
| [lowercase-name][] | Disallow capitalized test names | | ![fixable-green][] |
| [no-alias-methods][] | Disallow alias methods | ![style][] | ![fixable-green][] |
| [no-commented-out-tests][] | Disallow commented out tests | ![recommended][] | |
| [no-deprecated-functions][] | Disallow use of deprecated functions | | ![fixable-green][] |
| [no-disabled-tests][] | Disallow disabled tests | ![recommended][] | |
| [no-duplicate-hooks][] | Disallow duplicate hooks within a `describe` block | | |
| [no-expect-resolves][] | Disallow using `expect().resolves` | | |
Expand Down Expand Up @@ -170,6 +171,7 @@ https://github.com/dangreenisrael/eslint-plugin-jest-formatting
[lowercase-name]: docs/rules/lowercase-name.md
[no-alias-methods]: docs/rules/no-alias-methods.md
[no-commented-out-tests]: docs/rules/no-commented-out-tests.md
[no-deprecated-functions]: docs/rules/no-deprecated-functions.md
[no-disabled-tests]: docs/rules/no-disabled-tests.md
[no-duplicate-hooks]: docs/rules/no-duplicate-hooks.md
[no-expect-resolves]: docs/rules/no-expect-resolves.md
Expand Down
46 changes: 46 additions & 0 deletions docs/rules/no-deprecated-functions.md
@@ -0,0 +1,46 @@
# Disallow use of deprecated functions (no-deprecated-functions)

Over the years Jest has accrued some debt in the form of functions that have
either been renamed for clarity, or replaced with more powerful APIs.

While typically these deprecated functions are kept in the codebase for a number
of majors, eventually they are removed completely.

## Rule details

This rule warns about calls to deprecated functions, and provides details on
what to replace them with.

This rule can also autofix a number of these deprecations for you.

### `require.requireActual` & `require.requireMock`

These functions were replaced in Jest 21 and removed in Jest 26.

Originally, the `requireActual` & `requireMock` the `requireActual`&
`requireMock` functions were placed onto the `require` function.

These functions were later moved onto the `jest` object in order to be easier
for type checkers to handle, and their use via `require` deprecated. Finally,
the release of Jest 26 saw them removed from the `require` function all
together.

### `jest.addMatchers`

This function was replaced with `expect.extend` in Jest 17, and is scheduled for
removal in Jest 27.

### `jest.resetModuleRegistry`

This function was renamed to `resetModules` in Jest 15, and is scheduled for
removal in Jest 27.

### `jest.runTimersToTime`

This function was renamed to `advanceTimersByTime` in Jest 22, and is scheduled
for removal in Jest 27.

### `jest.genMockFromModule`

This function was renamed to `createMockFromModule` in Jest 26, and is scheduled
for removal in a future version of Jest.
1 change: 1 addition & 0 deletions src/__tests__/__snapshots__/rules.test.ts.snap
Expand Up @@ -15,6 +15,7 @@ Object {
"jest/lowercase-name": "error",
"jest/no-alias-methods": "error",
"jest/no-commented-out-tests": "error",
"jest/no-deprecated-functions": "error",
"jest/no-disabled-tests": "error",
"jest/no-duplicate-hooks": "error",
"jest/no-expect-resolves": "error",
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/rules.test.ts
Expand Up @@ -3,7 +3,7 @@ import { resolve } from 'path';
import plugin from '../';

const ruleNames = Object.keys(plugin.rules);
const numberOfRules = 40;
const numberOfRules = 41;

describe('rules', () => {
it('should have a corresponding doc for each rule', () => {
Expand Down
49 changes: 49 additions & 0 deletions src/rules/__tests__/no-deprecated-functions.ts
@@ -0,0 +1,49 @@
import { TSESLint } from '@typescript-eslint/experimental-utils';
import rule from '../no-deprecated-functions';

const ruleTester = new TSESLint.RuleTester();

[
['require.requireMock', 'jest.requireMock'],
['require.requireActual', 'jest.requireActual'],
['jest.addMatchers', 'expect.extend'],
['jest.resetModuleRegistry', 'jest.resetModules'],
['jest.runTimersToTime', 'jest.advanceTimersByTime'],
['jest.genMockFromModule', 'jest.createMockFromModule'],
].forEach(([deprecation, replacement]) => {
const [deprecatedName, deprecatedFunc] = deprecation.split('.');
const [replacementName, replacementFunc] = replacement.split('.');

ruleTester.run(`${deprecation} -> ${replacement}`, rule, {
valid: [
'jest',
'require("fs")',
`${replacement}()`,
replacement,
`${replacementName}['${replacementFunc}']()`,
`${replacementName}['${replacementFunc}']`,
],
invalid: [
{
code: `${deprecation}()`,
output: `${replacement}()`,
errors: [
{
messageId: 'deprecatedFunction',
data: { deprecation, replacement },
},
],
},
{
code: `${deprecatedName}['${deprecatedFunc}']()`,
output: `${replacementName}['${replacementFunc}']()`,
errors: [
{
messageId: 'deprecatedFunction',
data: { deprecation, replacement },
},
],
},
],
});
});
72 changes: 72 additions & 0 deletions src/rules/no-deprecated-functions.ts
@@ -0,0 +1,72 @@
import {
AST_NODE_TYPES,
TSESTree,
} from '@typescript-eslint/experimental-utils';
import { createRule, getNodeName } from './utils';

export default createRule({
name: __filename,
meta: {
docs: {
category: 'Best Practices',
description: 'Disallow use of deprecated functions',
recommended: false,
},
messages: {
deprecatedFunction:
'`{{ deprecation }}` has been deprecated in favor of `{{ replacement }}`',
},
type: 'suggestion',
schema: [],
fixable: 'code',
},
defaultOptions: [],
create(context) {
const deprecations: Record<string, string> = {
'require.requireMock': 'jest.requireMock',
'require.requireActual': 'jest.requireActual',
'jest.addMatchers': 'expect.extend',
'jest.resetModuleRegistry': 'jest.resetModules',
'jest.runTimersToTime': 'jest.advanceTimersByTime',
'jest.genMockFromModule': 'jest.createMockFromModule',
};

return {
CallExpression(node: TSESTree.CallExpression) {
if (node.callee.type !== AST_NODE_TYPES.MemberExpression) {
return;
}

const deprecation = getNodeName(node);

if (!deprecation || !(deprecation in deprecations)) {
return;
}

const replacement = deprecations[deprecation];
const { callee } = node;

context.report({
messageId: 'deprecatedFunction',
data: {
deprecation,
replacement,
},
node,
fix(fixer) {
let [name, func] = replacement.split('.');

if (callee.property.type === AST_NODE_TYPES.Literal) {
func = `'${func}'`;
}

return [
fixer.replaceText(callee.object, name),
fixer.replaceText(callee.property, func),
];
},
});
},
};
},
});