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 1 commit
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
43 changes: 43 additions & 0 deletions docs/rules/no-deprecated-functions.md
@@ -0,0 +1,43 @@
# Warns on usage of deprecated functions (no-deprecated-functions)

Over the years jest has accrued some debt in the form of functions that have
G-Rath marked this conversation as resolved.
Show resolved Hide resolved
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`
G-Rath marked this conversation as resolved.
Show resolved Hide resolved

These functions were removed in Jest 26.
G-Rath marked this conversation as resolved.
Show resolved Hide resolved

Originally in the early days of jest, the `requireActual`& `requireMock`
G-Rath marked this conversation as resolved.
Show resolved Hide resolved
functions were placed onto the `require` function.

These functions were later moved onto the `jest` global, and their use via
G-Rath marked this conversation as resolved.
Show resolved Hide resolved
`require` deprecated. Finally, the release of Jest 26 saw them removed from the
`require` function all together.

The PR implementing the removal can be found
[here](https://github.com/facebook/jest/pull/9854).
G-Rath marked this conversation as resolved.
Show resolved Hide resolved

### `jest.addMatchers`

This function has been replaced with `expect.extend`, and will ideally be
G-Rath marked this conversation as resolved.
Show resolved Hide resolved
removed in Jest 27.
G-Rath marked this conversation as resolved.
Show resolved Hide resolved

### `jest.resetModuleRegistry`

This function has been renamed to `resetModules`, and will ideally be removed in
Jest 27.
G-Rath marked this conversation as resolved.
Show resolved Hide resolved

### `jest.runTimersToTime`

This function has been renamed to `advanceTimersByTime`, and will ideally be
removed in Jest 27.
G-Rath marked this conversation as resolved.
Show resolved Hide resolved
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
48 changes: 48 additions & 0 deletions src/rules/__tests__/no-deprecated-functions.ts
@@ -0,0 +1,48 @@
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'],
].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 },
},
],
},
],
});
});
71 changes: 71 additions & 0 deletions src/rules/no-deprecated-functions.ts
@@ -0,0 +1,71 @@
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: 'Warns on usage 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',
};

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),
];
},
});
},
};
},
});