From f8624548476125e8811cffa008b8fe6001dbf55a Mon Sep 17 00:00:00 2001 From: Kevin Phelps Date: Tue, 24 Apr 2018 18:19:39 -0500 Subject: [PATCH 1/2] fix(rule): allow super lifecycle calls fixes #573 --- src/noLifeCycleCallRule.ts | 8 ++++++-- test/noLifeCycleCallRule.spec.ts | 25 +++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/noLifeCycleCallRule.ts b/src/noLifeCycleCallRule.ts index a9ef60bc0..c22642ccf 100644 --- a/src/noLifeCycleCallRule.ts +++ b/src/noLifeCycleCallRule.ts @@ -49,9 +49,13 @@ export class ExpressionCallMetadataWalker extends NgWalker { } private validateCallExpression(node: ts.CallExpression): void { - const name = (node.expression as any).name; + const name = ts.isPropertyAccessExpression(node.expression) ? node.expression.name : undefined; + const expression = ts.isPropertyAccessExpression(node.expression) ? node.expression.expression : undefined; - if (!name || !lifecycleHooksMethods.has(name.text)) { + const isSuperCall = expression && expression.kind === ts.SyntaxKind.SuperKeyword; + const isLifecycleCall = name && ts.isIdentifier(name) && lifecycleHooksMethods.has(name.text as any); + + if (isSuperCall || !isLifecycleCall) { return; } diff --git a/test/noLifeCycleCallRule.spec.ts b/test/noLifeCycleCallRule.spec.ts index fc3182125..763c593bd 100644 --- a/test/noLifeCycleCallRule.spec.ts +++ b/test/noLifeCycleCallRule.spec.ts @@ -58,7 +58,7 @@ describe(ruleName, () => { } `; - it(`should fail when explicitly call ${lifecycleHookMethod}`, () => { + it(`should fail when explicitly calling ${lifecycleHookMethodCall}`, () => { assertAnnotated({ ruleName, message: `Avoid explicitly calls to lifecycle hooks in class "${className}"`, @@ -85,7 +85,28 @@ describe(ruleName, () => { } `; - it(`should pass when call ${fakeMethod} method`, () => { + it(`should pass when calling ${fakeMethod} method`, () => { + assertSuccess(ruleName, source); + }); + }); + }); + } + + // call super lifecycle hook + for (const metadataKey of metadataKeys) { + describe(metadataKey, () => { + metadataPairs[metadataKey].forEach(lifecycleHookMethod => { + const lifecycleHookMethodCall = `super.${lifecycleHookMethod}()`; + const source = ` + @${metadataKey}() + class ${className} implements ${lifecycleHookMethod.slice(2)} { + ${lifecycleHookMethod}() { + ${lifecycleHookMethodCall} + } + } + `; + + it(`should pass when explicitly calling ${lifecycleHookMethodCall}`, () => { assertSuccess(ruleName, source); }); }); From 3e355c482f1900e321f8006b477d85dcd7cc9670 Mon Sep 17 00:00:00 2001 From: Kevin Phelps Date: Tue, 24 Apr 2018 18:30:41 -0500 Subject: [PATCH 2/2] fix(rule): prevent error when parent class node does not exist --- src/noLifeCycleCallRule.ts | 15 +++------------ test/noLifeCycleCallRule.spec.ts | 25 ++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/noLifeCycleCallRule.ts b/src/noLifeCycleCallRule.ts index c22642ccf..a29b77acd 100644 --- a/src/noLifeCycleCallRule.ts +++ b/src/noLifeCycleCallRule.ts @@ -14,7 +14,7 @@ export class Rule extends Lint.Rules.AbstractRule { typescriptOnly: true, }; - static FAILURE_STRING: string = 'Avoid explicitly calls to lifecycle hooks in class "%s"'; + static FAILURE_STRING: string = 'Avoid explicit calls to lifecycle hooks.'; public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { return this.applyWithWalker(new ExpressionCallMetadataWalker(sourceFile, this.getOptions())); @@ -55,17 +55,8 @@ export class ExpressionCallMetadataWalker extends NgWalker { const isSuperCall = expression && expression.kind === ts.SyntaxKind.SuperKeyword; const isLifecycleCall = name && ts.isIdentifier(name) && lifecycleHooksMethods.has(name.text as any); - if (isSuperCall || !isLifecycleCall) { - return; + if (isLifecycleCall && !isSuperCall) { + this.addFailureAtNode(node, Rule.FAILURE_STRING); } - - let currentNode = node as any; - - while (currentNode.parent.parent) { - currentNode = currentNode.parent; - } - - const failureConfig = [Rule.FAILURE_STRING, currentNode.name.text]; - this.addFailureAtNode(node, sprintf.apply(this, failureConfig)); } } diff --git a/test/noLifeCycleCallRule.spec.ts b/test/noLifeCycleCallRule.spec.ts index 763c593bd..9d8bdb176 100644 --- a/test/noLifeCycleCallRule.spec.ts +++ b/test/noLifeCycleCallRule.spec.ts @@ -61,13 +61,36 @@ describe(ruleName, () => { it(`should fail when explicitly calling ${lifecycleHookMethodCall}`, () => { assertAnnotated({ ruleName, - message: `Avoid explicitly calls to lifecycle hooks in class "${className}"`, + message: 'Avoid explicit calls to lifecycle hooks.', source }); }); }); }); } + + lifecycleHooksMethods.forEach(lifecycleHookMethod => { + const lifecycleHookMethodCall = `fixture.componentInstance.${lifecycleHookMethod}()`; + const totalTildes = '~'.repeat(lifecycleHookMethodCall.length); + const source = ` + it('should work', () => { + // test code... + + ${lifecycleHookMethodCall} + ${totalTildes} + + // more test code... + }) + `; + + it(`should fail when explicitly calling ${lifecycleHookMethod} outside of a class`, () => { + assertAnnotated({ + ruleName, + message: `Avoid explicit calls to lifecycle hooks.`, + source + }); + }); + }); }); describe('success', () => {