Skip to content

Commit 85899dc

Browse files
committed
feat(require-hook): support global checking
1 parent 005f23e commit 85899dc

File tree

2 files changed

+134
-20
lines changed

2 files changed

+134
-20
lines changed

src/rules/__tests__/require-hook.test.ts

+103
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,22 @@ ruleTester.run('require-hook', rule, {
1717
//
1818
});
1919
`,
20+
dedent`
21+
const { myFn } = require('../functions');
22+
23+
test('myFn', () => {
24+
expect(myFn()).toBe(1);
25+
});
26+
`,
27+
dedent`
28+
const { myFn } = require('../functions');
29+
30+
describe('myFn', () => {
31+
it('returns one', () => {
32+
expect(myFn()).toBe(1);
33+
});
34+
});
35+
`,
2036
dedent`
2137
describe('some tests', () => {
2238
it('is true', () => {
@@ -97,6 +113,16 @@ ruleTester.run('require-hook', rule, {
97113
`,
98114
],
99115
invalid: [
116+
{
117+
code: 'setup();',
118+
errors: [
119+
{
120+
messageId: 'useHook',
121+
line: 1,
122+
column: 1,
123+
},
124+
],
125+
},
100126
{
101127
code: dedent`
102128
describe('some tests', () => {
@@ -142,5 +168,82 @@ ruleTester.run('require-hook', rule, {
142168
},
143169
],
144170
},
171+
{
172+
code: dedent`
173+
import { database, isCity } from '../database';
174+
import { loadCities } from '../api';
175+
176+
jest.mock('../api');
177+
178+
const initializeCityDatabase = () => {
179+
database.addCity('Vienna');
180+
database.addCity('San Juan');
181+
database.addCity('Wellington');
182+
};
183+
184+
const clearCityDatabase = () => {
185+
database.clear();
186+
};
187+
188+
initializeCityDatabase();
189+
190+
test('that persists cities', () => {
191+
expect(database.cities.length).toHaveLength(3);
192+
});
193+
194+
test('city database has Vienna', () => {
195+
expect(isCity('Vienna')).toBeTruthy();
196+
});
197+
198+
test('city database has San Juan', () => {
199+
expect(isCity('San Juan')).toBeTruthy();
200+
});
201+
202+
describe('when loading cities from the api', () => {
203+
let consoleWarnSpy = jest.spyOn(console, 'warn');
204+
205+
loadCities.mockResolvedValue(['Wellington', 'London']);
206+
207+
it('does not duplicate cities', async () => {
208+
await database.loadCities();
209+
210+
expect(database.cities).toHaveLength(4);
211+
});
212+
213+
it('logs any duplicates', async () => {
214+
await database.loadCities();
215+
216+
expect(consoleWarnSpy).toHaveBeenCalledWith(
217+
'Ignored duplicate cities: Wellington',
218+
);
219+
});
220+
});
221+
222+
clearCityDatabase();
223+
`,
224+
parserOptions: { sourceType: 'module' },
225+
errors: [
226+
{
227+
messageId: 'useHook',
228+
line: 4,
229+
column: 1,
230+
},
231+
{
232+
messageId: 'useHook',
233+
line: 16,
234+
column: 1,
235+
},
236+
{
237+
messageId: 'useHook',
238+
line: 33,
239+
column: 3,
240+
},
241+
{
242+
messageId: 'useHook',
243+
line: 50,
244+
column: 1,
245+
},
246+
],
247+
},
145248
],
146249
});

src/rules/require-hook.ts

+31-20
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { AST_NODE_TYPES } from '@typescript-eslint/experimental-utils';
1+
import {
2+
AST_NODE_TYPES,
3+
TSESTree,
4+
} from '@typescript-eslint/experimental-utils';
25
import {
36
createRule,
47
isDescribeCall,
@@ -7,6 +10,18 @@ import {
710
isTestCaseCall,
811
} from './utils';
912

13+
const shouldBeInHook = (node: TSESTree.Node): boolean => {
14+
switch (node.type) {
15+
case AST_NODE_TYPES.ExpressionStatement:
16+
return shouldBeInHook(node.expression);
17+
case AST_NODE_TYPES.CallExpression:
18+
return !(isDescribeCall(node) || isTestCaseCall(node) || isHook(node));
19+
20+
default:
21+
return false;
22+
}
23+
};
24+
1025
export default createRule({
1126
name: __filename,
1227
meta: {
@@ -23,7 +38,21 @@ export default createRule({
2338
},
2439
defaultOptions: [],
2540
create(context) {
41+
const checkBlockBody = (body: TSESTree.BlockStatement['body']) => {
42+
for (const statement of body) {
43+
if (shouldBeInHook(statement)) {
44+
context.report({
45+
node: statement,
46+
messageId: 'useHook',
47+
});
48+
}
49+
}
50+
};
51+
2652
return {
53+
Program(program) {
54+
checkBlockBody(program.body);
55+
},
2756
CallExpression(node) {
2857
if (!isDescribeCall(node) || node.arguments.length < 2) {
2958
return;
@@ -38,25 +67,7 @@ export default createRule({
3867
return;
3968
}
4069

41-
for (const nod of testFn.body.body) {
42-
if (
43-
nod.type === AST_NODE_TYPES.ExpressionStatement &&
44-
nod.expression.type === AST_NODE_TYPES.CallExpression
45-
) {
46-
if (
47-
isDescribeCall(nod.expression) ||
48-
isTestCaseCall(nod.expression) ||
49-
isHook(nod.expression)
50-
) {
51-
return;
52-
}
53-
54-
context.report({
55-
node: nod.expression,
56-
messageId: 'useHook',
57-
});
58-
}
59-
}
70+
checkBlockBody(testFn.body.body);
6071
},
6172
};
6273
},

0 commit comments

Comments
 (0)