Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .vscode/eslint-plugin.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,4 @@
"path": ".."
}
],
"settings": {
"prettier.prettierPath": ".yarn/unplugged/prettier-npm-3.1.0-708d6027b1/node_modules/prettier"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was removed because we've reverted yarn to not use pnp (plug and play), so we leverage node_modules.

}
}
89 changes: 74 additions & 15 deletions src/rules/enforce-close-testing-module.rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,24 @@ function typeOfHook(hookName: TestBeforeHooks | TestAfterHooks): HookType {
return hookName.includes('All') ? 'all' : 'each';
}

export default createRule({
export type Options = [
{
closeAliases?: {
kind: string;
name: string;
}[];
},
];

const defaultOptions: Options = [
{
closeAliases: [],
},
];

export type MessageIds = 'testModuleNotClosed' | 'testModuleClosedInWrongHook';

export default createRule<Options, MessageIds>({
name: 'enforce-close-testing-module',
meta: {
type: 'problem',
Expand All @@ -27,15 +44,35 @@ export default createRule({
recommended: 'recommended',
},
fixable: undefined,
schema: [], // no options
schema: [
{
type: 'object',
properties: {
closeAliases: {
type: 'array',
items: {
type: 'object',
properties: {
kind: {
type: 'string',
},
name: {
type: 'string',
},
},
},
},
},
},
], // no options
messages: {
testModuleNotClosed:
'A Testing Module was created but not closed, which can cause memory leaks',
testModuleClosedInWrongHook:
'A Testing Module was created in {{ created }} but was closed in the wrong hook {{ closed }}',
},
},
defaultOptions: [],
defaultOptions,
create(context) {
let testModuleCreated = false;
let testModuleClosed = false;
Expand Down Expand Up @@ -120,18 +157,7 @@ export default createRule({
}

// Logic to check if module.close() is called in the wrong hook
const callExpressions = traverser.getAllParentCallExpressions(node);
const callExpressionWithHook = callExpressions.find(
(expression) =>
ASTUtils.isIdentifier(expression.callee) &&
['afterAll', 'afterEach'].includes(expression.callee.name)
);
if (
callExpressionWithHook &&
ASTUtils.isIdentifier(callExpressionWithHook.callee)
) {
closedInHook = callExpressionWithHook.callee.name as TestAfterHooks;
}
closedInHook = afterHookContainingNode(node);

if (
closedInHook &&
Expand All @@ -149,6 +175,19 @@ export default createRule({
});
}
},
'CallExpression[callee.type="Identifier"]': (
node: TSESTree.CallExpression
) => {
const calleeName = (node.callee as TSESTree.Identifier).name;
const functionAliases = context.options[0]?.closeAliases?.filter(
(alias) => alias.kind === 'function'
);

if (functionAliases?.some((alias) => alias.name === calleeName)) {
testModuleClosed = true;
closedInHook = afterHookContainingNode(node);
}
},
'Program:exit': (node) => {
if (testModuleCreated && !testModuleClosed && !appModuleClosed) {
context.report({
Expand All @@ -161,3 +200,23 @@ export default createRule({
};
},
});

function afterHookContainingNode(
node: TSESTree.CallExpression | TSESTree.MemberExpression
): TestAfterHooks | undefined {
let result: TestAfterHooks | undefined;
const callExpressions = traverser.getAllParentCallExpressions(node);
const callExpressionWithHook = callExpressions.find(
(expression) =>
ASTUtils.isIdentifier(expression.callee) &&
['afterAll', 'afterEach'].includes(expression.callee.name)
);
if (
callExpressionWithHook &&
ASTUtils.isIdentifier(callExpressionWithHook.callee)
) {
result = callExpressionWithHook.callee.name as TestAfterHooks;
}
return result;
}

28 changes: 28 additions & 0 deletions tests/rules/enforce-close-testing-module.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,34 @@ ruleTester.run('enforce-close-testing-module', enforceCloseTestingModuleRule, {
});
`,
},
{
code: `
describe('Closes the testingModule using a custom function', () => {
let testingModule: TestingModule;
beforeEach(async () => {
testingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
});
it('should be defined', () => {
expect(testingModule).toBeDefined();
});
});
afterEach(async () => {
await customClose();
});
`,
options: [
{
closeAliases: [
{
kind: 'function',
name: 'customClose',
},
],
},
],
},
],
invalid: [
{
Expand Down