Skip to content

Commit 73a5d36

Browse files
committed
feat: create no-deprecated-functions rule
1 parent f6e0bd0 commit 73a5d36

File tree

5 files changed

+164
-1
lines changed

5 files changed

+164
-1
lines changed

docs/rules/no-deprecated-functions.md

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Warns on usage of deprecated functions (no-deprecated-functions)
2+
3+
Over the years jest has accrued some debt in the form of functions that have
4+
either been renamed for clarity, or replaced with more powerful APIs.
5+
6+
While typically these deprecated functions are kept in the codebase for a number
7+
of majors, eventually they are removed completely.
8+
9+
## Rule details
10+
11+
This rule warns about calls to deprecated functions, and provides details on
12+
what to replace them with.
13+
14+
This rule can also autofix a number of these deprecations for you.
15+
16+
### `require.requireActual.` & `require.requireMock`
17+
18+
These functions were removed in Jest 26.
19+
20+
Originally in the early days of jest, the `requireActual`& `requireMock`
21+
functions were placed onto the `require` function.
22+
23+
These functions were later moved onto the `jest` global, and their use via
24+
`require` deprecated. Finally, the release of Jest 26 saw them removed from the
25+
`require` function all together.
26+
27+
The PR implementing the removal can be found
28+
[here](https://github.com/facebook/jest/pull/9854).
29+
30+
### `jest.addMatchers`
31+
32+
This function has been replaced with `expect.extend`, and will ideally be
33+
removed in Jest 27.
34+
35+
### `jest.resetModuleRegistry`
36+
37+
This function has been renamed to `resetModules`, and will ideally be removed in
38+
Jest 27.
39+
40+
### `jest.runTimersToTime`
41+
42+
This function has been renamed to `advanceTimersByTime`, and will ideally be
43+
removed in Jest 27.

src/__tests__/__snapshots__/rules.test.ts.snap

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Object {
1515
"jest/lowercase-name": "error",
1616
"jest/no-alias-methods": "error",
1717
"jest/no-commented-out-tests": "error",
18+
"jest/no-deprecated-functions": "error",
1819
"jest/no-disabled-tests": "error",
1920
"jest/no-duplicate-hooks": "error",
2021
"jest/no-expect-resolves": "error",

src/__tests__/rules.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { resolve } from 'path';
33
import plugin from '../';
44

55
const ruleNames = Object.keys(plugin.rules);
6-
const numberOfRules = 40;
6+
const numberOfRules = 41;
77

88
describe('rules', () => {
99
it('should have a corresponding doc for each rule', () => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { TSESLint } from '@typescript-eslint/experimental-utils';
2+
import rule from '../no-deprecated-functions';
3+
4+
const ruleTester = new TSESLint.RuleTester();
5+
6+
[
7+
['require.requireMock', 'jest.requireMock'],
8+
['require.requireActual', 'jest.requireActual'],
9+
['jest.addMatchers', 'expect.extend'],
10+
['jest.resetModuleRegistry', 'jest.resetModules'],
11+
['jest.runTimersToTime', 'jest.advanceTimersByTime'],
12+
].forEach(([deprecation, replacement]) => {
13+
const [deprecatedName, deprecatedFunc] = deprecation.split('.');
14+
const [replacementName, replacementFunc] = replacement.split('.');
15+
16+
ruleTester.run(`${deprecation} -> ${replacement}`, rule, {
17+
valid: [
18+
'jest',
19+
'require("fs")',
20+
`${replacement}()`,
21+
replacement,
22+
`${replacementName}['${replacementFunc}']()`,
23+
`${replacementName}['${replacementFunc}']`,
24+
],
25+
invalid: [
26+
{
27+
code: `${deprecation}()`,
28+
output: `${replacement}()`,
29+
errors: [
30+
{
31+
messageId: 'deprecatedFunction',
32+
data: { deprecation, replacement },
33+
},
34+
],
35+
},
36+
{
37+
code: `${deprecatedName}['${deprecatedFunc}']()`,
38+
output: `${replacementName}['${replacementFunc}']()`,
39+
errors: [
40+
{
41+
messageId: 'deprecatedFunction',
42+
data: { deprecation, replacement },
43+
},
44+
],
45+
},
46+
],
47+
});
48+
});

src/rules/no-deprecated-functions.ts

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import {
2+
AST_NODE_TYPES,
3+
TSESTree,
4+
} from '@typescript-eslint/experimental-utils';
5+
import { createRule, getNodeName } from './utils';
6+
7+
export default createRule({
8+
name: __filename,
9+
meta: {
10+
docs: {
11+
category: 'Best Practices',
12+
description: 'Warns on usage of deprecated functions',
13+
recommended: false,
14+
},
15+
messages: {
16+
deprecatedFunction:
17+
'`{{ deprecation }}` has been deprecated in favor of `{{ replacement }}`',
18+
},
19+
type: 'suggestion',
20+
schema: [],
21+
fixable: 'code',
22+
},
23+
defaultOptions: [],
24+
create(context) {
25+
const deprecations: Record<string, string> = {
26+
'require.requireMock': 'jest.requireMock',
27+
'require.requireActual': 'jest.requireActual',
28+
'jest.addMatchers': 'expect.extend',
29+
'jest.resetModuleRegistry': 'jest.resetModules',
30+
'jest.runTimersToTime': 'jest.advanceTimersByTime',
31+
};
32+
33+
return {
34+
CallExpression(node: TSESTree.CallExpression) {
35+
if (node.callee.type !== AST_NODE_TYPES.MemberExpression) {
36+
return;
37+
}
38+
39+
const deprecation = getNodeName(node);
40+
41+
if (!deprecation || !(deprecation in deprecations)) {
42+
return;
43+
}
44+
45+
const replacement = deprecations[deprecation];
46+
const { callee } = node;
47+
48+
context.report({
49+
messageId: 'deprecatedFunction',
50+
data: {
51+
deprecation,
52+
replacement,
53+
},
54+
node,
55+
fix(fixer) {
56+
let [name, func] = replacement.split('.');
57+
58+
if (callee.property.type === AST_NODE_TYPES.Literal) {
59+
func = `'${func}'`;
60+
}
61+
62+
return [
63+
fixer.replaceText(callee.object, name),
64+
fixer.replaceText(callee.property, func),
65+
];
66+
},
67+
});
68+
},
69+
};
70+
},
71+
});

0 commit comments

Comments
 (0)